From 977f701eef48abbfd5d7884105a74f2ec05d3501 Mon Sep 17 00:00:00 2001 From: RedDeadDepresso <94017243+RedDeadDepresso@users.noreply.github.com> Date: Wed, 24 Jul 2024 03:46:19 +0100 Subject: [PATCH] fix: checkboxes changes saved --- app/common/config.py | 44 +------- app/common/file_manager.py | 154 +++++++++++++------------- app/common/logger.py | 69 ++++++++---- app/components/navigation_checkbox.py | 17 ++- app/modules/base.py | 17 --- app/modules/create_backup.py | 33 +++--- app/modules/fc_kks.py | 119 ++++++++++---------- app/modules/handler.py | 15 +++ app/modules/install_chara.py | 71 ++++++------ app/modules/remove_chara.py | 55 ++++----- app/modules/request.py | 73 ++++++++++++ app/view/logger_interface.py | 6 +- app/view/main_window.py | 8 +- script.py | 16 +-- 14 files changed, 388 insertions(+), 309 deletions(-) delete mode 100644 app/modules/base.py create mode 100644 app/modules/handler.py create mode 100644 app/modules/request.py diff --git a/app/common/config.py b/app/common/config.py index ba5b562..b85370f 100644 --- a/app/common/config.py +++ b/app/common/config.py @@ -60,13 +60,13 @@ class Config(QConfig): # fckks fckksEnable = ConfigItem( - "FCKKS", "Enable", False, BoolValidator() + "FilterConvertKKS", "Enable", False, BoolValidator() ) fccksPath = ConfigItem( - "FCKKS", "InputPath", "", FolderValidator() + "FilterConvertKKS", "InputPath", "", FolderValidator() ) convert = ConfigItem( - "FCKKS", "Convert", False, BoolValidator() + "FilterConvertKKS", "Convert", False, BoolValidator() ) # installChara @@ -102,44 +102,6 @@ class Config(QConfig): # software update checkUpdateAtStartUp = ConfigItem("Update", "CheckUpdateAtStartUp", True, BoolValidator()) - # def validate(self): - # Logger.log_info("SCRIPT", "Validating config") - # self.ok = True - # self.tasks = self.config_data["Core"]["Tasks"] - # self.create_gamepath() - - # for task in self.tasks: - # if self.config_data[task]["Enable"]: - # if "InputPath" in self.config_data[task]: - # path = self.config_data[task]["InputPath"] - # elif "OutputPath" in self.config_data[task]: - # path = self.config_data[task]["OutputPath"] - # if not os.path.exists(path): - # Logger.log_error("SCRIPT", f"Path invalid for task {task}") - # raise Exception() - - # self.install_chara = self.config_data.get("InstallChara", {}) - # self.create_backup = self.config_data.get("CreateBackup", {}) - # self.remove_chara = self.config_data.get("RemoveChara", {}) - # self.fc_kks = self.config_data.get("FCKKS", {}) - - # def create_gamepath(self): - # base = self.config_data["Core"]["GamePath"] - # self.game_path = { - # "base": base, - # "UserData": os.path.join(base, "UserData"), - # "BepInEx": os.path.join(base, "BepInEx"), - # "mods": os.path.join(base, "mods"), - # "chara": os.path.join(base, "UserData\\chara\\female"), - # "coordinate": os.path.join(base, "UserData\coordinate"), - # "Overlays": os.path.join(base, "UserData\Overlays") - # } - - # for path in self.game_path.values(): - # if not os.path.exists(path): - # Logger.log_error("SCRIPT", "Game path not valid") - # raise Exception() - YEAR = 2023 AUTHOR = "zhiyiYo" diff --git a/app/common/file_manager.py b/app/common/file_manager.py index 84d8de2..2286cb3 100644 --- a/app/common/file_manager.py +++ b/app/common/file_manager.py @@ -14,98 +14,98 @@ class FileManager: def __init__(self, config): self.config = config - def find_all_files(self, directory): - file_list = [] - compressed_file_list = [] - compressed_extensions = [".rar", ".zip", ".7z"] + def findAllFiles(self, directory): + fileList = [] + archiveList = [] + archiveExtensions = [".rar", ".zip", ".7z"] for root, dirs, files in os.walk(directory): for file in files: - file_path = os.path.join(root, file) - file_size = os.path.getsize(file_path) - _, file_extension = os.path.splitext(file_path) + filePath = os.path.join(root, file) + fileSize = os.path.getsize(filePath) + _, fileExtension = os.path.splitext(filePath) - if file_extension in compressed_extensions: - compressed_file_list.append((file_path, file_size, file_extension)) + if fileExtension in archiveExtensions: + archiveList.append((filePath, fileSize, fileExtension)) else: - file_list.append((file_path, file_size, file_extension)) + fileList.append((filePath, fileSize, fileExtension)) - file_list.sort(key=lambda x: x[1]) - compressed_file_list.sort(key=lambda x: x[1]) - return file_list, compressed_file_list + fileList.sort(key=lambda x: x[1]) + archiveList.sort(key=lambda x: x[1]) + return fileList, archiveList - def copy_and_paste(self, type, source_path, destination_folder): - source_path = source_path[0] - base_name = os.path.basename(source_path) - destination_path = os.path.join(destination_folder, base_name) + def copyAndPaste(self, type, sourcePath, destinationFolder): + sourcePath = sourcePath[0] + baseName = os.path.basename(sourcePath) + destinationPath = os.path.join(destinationFolder, baseName) conflicts = self.config.install_chara["FileConflicts"] - already_exists = os.path.exists(destination_path) + alreadyExists = os.path.exists(destinationPath) - if already_exists and conflicts == "Skip": - logger.log_skipped(type, base_name) + if alreadyExists and conflicts == "Skip": + logger.skipped(type, baseName) return - elif already_exists and conflicts == "Replace": - logger.log_replaced(type, base_name) + elif alreadyExists and conflicts == "Replace": + logger.replaced(type, baseName) - elif already_exists and conflicts == "Rename": - max_retries = 3 - for attempt in range(max_retries): + elif alreadyExists and conflicts == "Rename": + maxRetries = 3 + for attempt in range(maxRetries): try: - filename, file_extension = os.path.splitext(source_path) - new_name = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f') - new_source_path = f"{filename}_{new_name}{file_extension}" - os.rename(source_path, new_source_path) - source_path = new_source_path - logger.log_renamed(type, base_name) + filename, fileExtension = os.path.splitext(sourcePath) + newName = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f') + newSourcePath = f"{filename}_{newName}{fileExtension}" + os.rename(sourcePath, newSourcePath) + sourcePath = newSourcePath + logger.renamed(type, baseName) - filename, file_extension = os.path.splitext(destination_path) - destination_path = f"{filename}_{new_name}{file_extension}" + filename, fileExtension = os.path.splitext(destinationPath) + destinationPath = f"{filename}_{newName}{fileExtension}" break # Exit the loop if renaming is successful except PermissionError: - if attempt < max_retries - 1: + if attempt < maxRetries - 1: time.sleep(1) # Wait for 1 second before retrying else: - logger.log_error(type, f"Failed to rename {base_name} after {max_retries} attempts.") + logger.error(type, f"Failed to rename {baseName} after {maxRetries} attempts.") return try: - shutil.copy(source_path, destination_path) - print(f"File copied successfully from {source_path} to {destination_path}") - if not already_exists: - logger.log_success(type, base_name) + shutil.copy(sourcePath, destinationPath) + print(f"File copied successfully from {sourcePath} to {destinationPath}") + if not alreadyExists: + logger.success(type, baseName) except FileNotFoundError: - logger.log_error(type, f"{base_name} does not exist.") + logger.error(type, f"{baseName} does not exist.") except PermissionError: - logger.log_error(type, f"Permission denied for {base_name}") + logger.error(type, f"Permission denied for {baseName}") except Exception as e: - logger.log_error(type, f"An error occurred: {e}") + logger.error(type, f"An error occurred: {e}") - def find_and_remove(self, type, source_path, destination_folder): - source_path = source_path[0] - base_name = os.path.basename(source_path) - destination_path = os.path.join(destination_folder, base_name) - if os.path.exists(destination_path): + def findAndRemove(self, type, sourcePath, destinationFolder): + sourcePath = sourcePath[0] + baseName = os.path.basename(sourcePath) + destinationPath = os.path.join(destinationFolder, baseName) + if os.path.exists(destinationPath): try: - os.remove(destination_path) - logger.log_removed(type, base_name) + os.remove(destinationPath) + logger.removed(type, baseName) except OSError as e: - logger.log_error(type, base_name) + logger.error(type, baseName) - def create_archive(self, folders, archive_path): + def createArchive(self, folders, archivePath): # Specify the full path to the 7zip executable - path_to_7zip = patoolib.util.find_program("7z") - if not path_to_7zip: - logger.log_error("SCRIPT", "7zip not found. Unable to create backup") + pathTo7zip = patoolib.util.find_program("7z") + if not pathTo7zip: + logger.error("SCRIPT", "7zip not found. Unable to create backup") raise Exception() - if os.path.exists(archive_path+".7z"): - os.remove(archive_path+".7z") + if os.path.exists(archivePath+".7z"): + os.remove(archivePath+".7z") - path_to_7zip = f'"{path_to_7zip}"' - archive_path = f'"{archive_path}"' - exclude_folders = [ + pathTo7zip = f'"{pathTo7zip}"' + archivePath = f'"{archivePath}"' + excludeFolders = [ '"Sideloader Modpack"', '"Sideloader Modpack - Studio"', '"Sideloader Modpack - KK_UncensorSelector"', @@ -118,52 +118,52 @@ class FileManager: ] # Create a string of folder names to exclude - exclude_string = '' - for folder in exclude_folders: - exclude_string += f'-xr!{folder} ' + excludeString = '' + for folder in excludeFolders: + excludeString += f'-xr!{folder} ' # Create a string of folder names to include - include_string = '' + includeString = '' for folder in folders: - include_string += f'"{folder}" ' + includeString += f'"{folder}" ' # Construct the 7zip command - command = f'{path_to_7zip} a -t7z {archive_path} {include_string} {exclude_string}' + command = f'{pathTo7zip} a -t7z {archivePath} {includeString} {excludeString}' # Call the command process = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Print the output for line in process.stdout.decode().split('\n'): if line.strip() != "": - logger.log_info("7-Zip", line) + logger.info("7-Zip", line) # Check the return code if process.returncode not in [0, 1]: raise Exception() - def extract_archive(self, archive_path): + def extractArchive(self, archivePath): try: - archive_name = os.path.basename(archive_path) - logger.log_info("ARCHIVE", f"Extracting {archive_name}") - extract_path = os.path.join(f"{os.path.splitext(archive_path)[0]}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')}") - patoolib.extract_archive(archive_path, outdir=extract_path) - return extract_path + archiveName = os.path.basename(archivePath) + logger.info("ARCHIVE", f"Extracting {archiveName}") + extractPath = os.path.join(f"{os.path.splitext(archivePath)[0]}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')}") + patoolib.extract_archive(archivePath, outdir=extractPath) + return extractPath except patoolib.util.PatoolError as e: - text = f"There is an error with the archive {archive_name} but it is impossible to detect the cause. Maybe it requires a password?" + text = f"There is an error with the archive {archiveName} but it is impossible to detect the cause. Maybe it requires a password?" while self.config.install_chara["Password"] == "Request Password": try: dialog = customtkinter.CTkInputDialog(text=text, title="Enter Password") password = dialog.get_input() if password is not None or "": - patoolib.extract_archive(archive_path, outdir=extract_path, password=password) - return extract_path + patoolib.extract_archive(archivePath, outdir=extractPath, password=password) + return extractPath else: break except: - text = f"Wrong password or {archive_name} is corrupted. Please enter password again or click Cancel" + text = f"Wrong password or {archiveName} is corrupted. Please enter password again or click Cancel" - logger.log_skipped("ARCHIVE", archive_name) + logger.skipped("ARCHIVE", archiveName) fileManager = FileManager() \ No newline at end of file diff --git a/app/common/logger.py b/app/common/logger.py index bdfd389..b66f226 100644 --- a/app/common/logger.py +++ b/app/common/logger.py @@ -26,7 +26,7 @@ class Logger: self.logger.setLevel(logging.INFO) self.logger.addHandler(handler1) - def __out__(self, message: str, level: int = 1, raw_print=False) -> None: + def __out__(self, category: str, message: str, level: int = 1, raw_print=False) -> None: """ Output log :param message: log message @@ -41,10 +41,13 @@ class Logger: while len(logging.root.handlers) > 0: logging.root.handlers.pop() - # Status Text: INFO, WARNING, ERROR, CRITICAL - status = ['    INFO', ' WARNING', '   ERROR', 'CRITICAL'] - # Status Color: Blue, Orange, Red, Purple - statusColor = ['#2d8cf0', '#f90', '#ed3f14', '#3e0480'] + # Status Text: INFO, SUCCESS, ERROR, SKIPPED, REPLACED, RENAMED, REMOVED + status = ['    INFO', '  SUCCESS', '   ERROR', + ' SKIPPED', ' REPLACED', ' RENAMED', ' REMOVED'] + + # Status Color: Blue, Red, Green, Orange, + statusColor = ['#2d8cf0', '#ed3f14', '#f90', '#f90', '#f90', '#f90', '#00c12b'] + # Status HTML: status statusHtml = [ f'{status}' @@ -55,7 +58,7 @@ class Logger: message = message.replace('\n', '
').replace(' ', ' ') adding = (f'''
- {statusHtml[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message} + {statusHtml[level - 1]} | {category} | {message}
''') self.logs += adding @@ -63,37 +66,61 @@ class Logger: else: print(f'{statusHtml[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message}') - def info(self, message: str) -> None: + def info(self, category: str, message: str) -> None: """ :param message: log message Output info log """ - self.__out__(message, 1) + self.__out__(category, message, 1) - def warning(self, message: str) -> None: - """ - :param message: log message - - Output warn log - """ - self.__out__(message, 2) - - def error(self, message: Union[str, Exception]) -> None: + def success(self, category: str, message: Union[str, Exception]) -> None: """ :param message: log message Output error log """ - self.__out__(message, 3) + self.__out__(category, message, 2) - def critical(self, message: str) -> None: + def error(self, category: str, message: Union[str, Exception]) -> None: """ :param message: log message - Output critical log + Output error log """ - self.__out__(message, 4) + self.__out__(category, message, 3) + + def skipped(self, category: str, message: str) -> None: + """ + :param message: log message + + Output warn log + """ + self.__out__(category, message, 4) + + def replaced(self, category: str, message: str) -> None: + """ + :param message: log message + + Output warn log + """ + self.__out__(category, message, 5) + + def renamed(self, category: str, message: str) -> None: + """ + :param message: log message + + Output warn log + """ + self.__out__(category, message, 6) + + def removed(self, category: str, message: str) -> None: + """ + :param message: log message + + Output warn log + """ + self.__out__(category, message, 6) def line(self) -> None: """ diff --git a/app/components/navigation_checkbox.py b/app/components/navigation_checkbox.py index 1c5255b..2fcdd3b 100644 --- a/app/components/navigation_checkbox.py +++ b/app/components/navigation_checkbox.py @@ -6,13 +6,16 @@ from qfluentwidgets import ToolButton, CheckBox from qfluentwidgets.common.icon import FluentIcon as FIF from app.common.signal_bus import signalBus +from app.common.config import cfg class NavigationCheckBox(QWidget): - def __init__(self, text, settingGroup) -> None: + def __init__(self, text, configItem, settingGroup) -> None: super().__init__() self.layout = QHBoxLayout() self.checkBox = CheckBox(text) + self.configItem = configItem + self.loadCheckState() self.toolButton = ToolButton(FIF.SETTING) self.settingGroup = settingGroup @@ -25,10 +28,22 @@ class NavigationCheckBox(QWidget): self.setLayout(self.layout) def __connectSignalToSlot(self): + self.checkBox.stateChanged.connect(self.saveCheckState) self.toolButton.clicked.connect(lambda: signalBus.switchToSettingGroup.emit(self.settingGroup)) signalBus.selectAllClicked.connect(lambda: self.checkBox.setChecked(True)) signalBus.clearAllClicked.connect(lambda: self.checkBox.setChecked(False)) + def loadCheckState(self): + value = cfg.get(self.configItem) + if value: + self.checkBox.setCheckState(Qt.CheckState.Checked) + else: + self.checkBox.setCheckState(Qt.CheckState.Unchecked) + + def saveCheckState(self, state): + isChecked = state > 0 + cfg.set(self.configItem, isChecked) + diff --git a/app/modules/base.py b/app/modules/base.py deleted file mode 100644 index 04f4444..0000000 --- a/app/modules/base.py +++ /dev/null @@ -1,17 +0,0 @@ -from app.common.config import cfg - - -class Request: - def __init__(self) -> None: - pass - - -class Handler: - def __init__(self) -> None: - pass - - def set_next(self): - pass - - def handle(self): - pass \ No newline at end of file diff --git a/app/modules/create_backup.py b/app/modules/create_backup.py index 1cc36a3..afe0632 100644 --- a/app/modules/create_backup.py +++ b/app/modules/create_backup.py @@ -1,24 +1,25 @@ import os -class CreateBackup: - def __init__(self, config, file_manager): - """Initializes the Bounty module. +from app.common.file_manager import fileManager +from app.modules.handler import Handler - Args: - config (Config): BAAuto Config instance - """ - self.config = config - self.file_manager = file_manager - self.backup_folders = self.config.create_backup["GameFolders"] - self.filename = self.config.create_backup["Filename"] - self.output_path = self.config.create_backup["OutputPath"] - self.game_path = self.config.game_path + +class CreateBackup(Handler): + def __str__(self) -> str: + return "Create Backup" - def logic_wrapper(self): - selected_folders = [self.game_path[folder] for folder in self.backup_folders if self.backup_folders[folder]] - output_path = os.path.join(self.output_path, self.filename) - self.file_manager.create_archive(selected_folders, output_path) + def loadConfig(self, config): + super().loadConfig(config) + folders = ["mods", "UserData", "BepInEx"] + self.folders = [self.gamePath[f] for f in folders if self.config[f]] + self.outputPath = self.config["OutputPath"] + self.filename = self.config["Filename"] + + def handle(self, request): + outputPath = os.path.join(self.outputPath, self.filename) + fileManager.createArchive(self.folders, outputPath) + self.setNext(request) diff --git a/app/modules/fc_kks.py b/app/modules/fc_kks.py index 992d041..b363c92 100644 --- a/app/modules/fc_kks.py +++ b/app/modules/fc_kks.py @@ -2,106 +2,107 @@ import os import re as regex import codecs import shutil -from util.logger import Logger -class FilterConvertKKS: - def __init__(self, config, file_manager): - """Initializes the Bounty module. +from app.common.logger import logger +from app.modules.handler import Handler - Args: - config (Config): BAAuto Config instance - """ - self.config = config - self.file_manager = file_manager - self.convert = self.config.fc_kks["Convert"] - def get_list(self, folder_path): - new_list = [] - for root, dirs, files in os.walk(folder_path): +class FilterConvertKKS(Handler): + def __str__(self) -> str: + return "Filter Convert KKS" + + def loadConfig(self, config): + super().loadConfig(config) + self.convert = self.config["Convert"] + + def getList(self, folderPath): + newList = [] + for root, dirs, files in os.walk(folderPath): for filename in files: if regex.match(r".*(\.png)$", filename): - new_list.append(os.path.join(root, filename)) - return new_list + newList.append(os.path.join(root, filename)) + return newList - def check_png(self, card_path): - with codecs.open(card_path, "rb") as card: + def checkPng(self, cardPath): + with codecs.open(cardPath, "rb") as card: data = card.read() - card_type = 0 + cardType = 0 if data.find(b"KoiKatuChara") != -1: - card_type = 1 + cardType = 1 if data.find(b"KoiKatuCharaSP") != -1: - card_type = 2 + cardType = 2 elif data.find(b"KoiKatuCharaSun") != -1: - card_type = 3 - Logger.log_info(f"[{card_type}]", f"{card_path}") - return card_type + cardType = 3 + logger.info(f"[{cardType}]", f"{cardPath}") + return cardType - def convert_kk(self, card_name, card_path, destination_path): - with codecs.open(card_path, mode="rb") as card: + def convertKk(self, cardName, cardPath, destinationPath): + with codecs.open(cardPath, mode="rb") as card: data = card.read() - replace_list = [ + replaceList = [ [b"\x15\xe3\x80\x90KoiKatuCharaSun", b"\x12\xe3\x80\x90KoiKatuChara"], [b"Parameter\xa7version\xa50.0.6", b"Parameter\xa7version\xa50.0.5"], [b"version\xa50.0.6\xa3sex", b"version\xa50.0.5\xa3sex"], ] - for text in replace_list: + for text in replaceList: data = data.replace(text[0], text[1]) - new_file_path = os.path.normpath(os.path.join(destination_path, f"KKS2KK_{card_name}")) - # print(f"new_file_path {new_file_path}") + newFilePath = os.path.normpath(os.path.join(destinationPath, f"KKS2KK_{cardName}")) - with codecs.open(new_file_path, "wb") as new_card: - new_card.write(data) + with codecs.open(newFilePath, "wb") as newCard: + newCard.write(data) - def logic_wrapper(self): + def handle(self, request): path = self.config.fc_kks["InputPath"] - kks_card_list = [] - kks_folder = "_KKS_card_" - kks_folder2 = "_KKS_to_KK_" + kksCardList = [] + kksFolder = "_KKS_card_" + kksFolder2 = "_KKS_to_KK_" - png_list = self.get_list(path) + pngList = self.getList(path) - count = len(png_list) + count = len(pngList) if count > 0: - Logger.log_info("SCRIPT", "0: unknown / 1: kk / 2: kksp / 3: kks") - for png in png_list: - if self.check_png(png) == 3: - kks_card_list.append(png) + logger.info("SCRIPT", "0: unknown / 1: kk / 2: kksp / 3: kks") + for png in pngList: + if self.checkPng(png) == 3: + kksCardList.append(png) else: - Logger.log_success("SCRIPT", f"no PNG found") + logger.success("SCRIPT", f"no PNG found") return - count = len(kks_card_list) + count = len(kksCardList) if count > 0: - print(kks_card_list) + print(kksCardList) - target_folder = os.path.normpath(os.path.join(path, kks_folder)) - target_folder2 = os.path.normpath(os.path.join(path, kks_folder2)) - if not os.path.isdir(target_folder): - os.mkdir(target_folder) + targetFolder = os.path.normpath(os.path.join(path, kksFolder)) + targetFolder2 = os.path.normpath(os.path.join(path, kksFolder2)) + if not os.path.isdir(targetFolder): + os.mkdir(targetFolder) if self.convert: - Logger.log_info("SCRIPT", f"Conversion to KK is [{self.convert}]") - if not os.path.isdir(target_folder2): - os.mkdir(target_folder2) + logger.info("SCRIPT", f"Conversion to KK is [{self.convert}]") + if not os.path.isdir(targetFolder2): + os.mkdir(targetFolder2) - for card_path in kks_card_list: - source = card_path - card = os.path.basename(card_path) - target = os.path.normpath(os.path.join(target_folder, card)) + for cardPath in kksCardList: + source = cardPath + card = os.path.basename(cardPath) + target = os.path.normpath(os.path.join(targetFolder, card)) # copy & convert before move if self.convert: - self.convert_kk(card, source, target_folder2) + self.convertKk(card, source, targetFolder2) # move file shutil.move(source, target) if self.convert: - Logger.log_success("SCRIPT", f"[{count}] cards moved to [{kks_folder}] folder, converted and save to [{kks_folder2}] folder") + logger.success("SCRIPT", f"[{count}] cards moved to [{kksFolder}] folder, converted and save to [{kksFolder2}] folder") else: - Logger.log_success("SCRIPT", f"[{count}] cards moved to [{kks_folder}] folder") + logger.success("SCRIPT", f"[{count}] cards moved to [{kksFolder}] folder") else: - Logger.log_success("SCRIPT", f"no KKS card found") + logger.success("SCRIPT", f"no KKS card found") + + self.setNext(request) \ No newline at end of file diff --git a/app/modules/handler.py b/app/modules/handler.py new file mode 100644 index 0000000..1dbfa9c --- /dev/null +++ b/app/modules/handler.py @@ -0,0 +1,15 @@ +class Handler: + def __str__(self) -> str: + return "Handler" + + def loadConfig(self, config): + self.gamePath = config.get("Core", "GamePath") + self.config = config.get(str(self)) + + def handle(self, request): + pass + + def setNext(self, request): + request.removeHandler() + request.process() + diff --git a/app/modules/install_chara.py b/app/modules/install_chara.py index 05f12b1..3f9d1bb 100644 --- a/app/modules/install_chara.py +++ b/app/modules/install_chara.py @@ -1,56 +1,57 @@ import os import codecs -from util.logger import Logger -class InstallChara: - def __init__(self, config, file_manager): - """Initializes the Bounty module. +from app.common.file_manager import fileManager +from app.common.logger import logger +from app.modules.handler import Handler - Args: - config (Config): BAAuto Config instance - """ - self.config = config - self.file_manager = file_manager - self.game_path = self.config.game_path - self.input_path = self.config.install_chara["InputPath"] - def resolve_png(self, image_path): - with codecs.open(image_path[0], "rb") as card: +class InstallChara(Handler): + def __str__(self) -> str: + return "Install Chara" + + def loadConfig(self, config): + super().loadConfig(config) + self.inputPath = self.config["InputPath"] + + def resolvePng(self, imagePath): + with codecs.open(imagePath[0], "rb") as card: data = card.read() if data.find(b"KoiKatuChara") != -1: if data.find(b"KoiKatuCharaSP") != -1 or data.find(b"KoiKatuCharaSun") != -1: - basename = os.path.basename(image_path[0]) - Logger.log_error("CHARA", f"{basename} is a KKS card") + basename = os.path.basename(imagePath[0]) + logger.error("CHARA", f"{basename} is a KKS card") return - self.file_manager.copy_and_paste("CHARA", image_path, self.game_path["chara"]) + fileManager.copyAndPaste("CHARA", imagePath, self.gamePath["chara"]) elif data.find(b"KoiKatuClothes") != -1: - self.file_manager.copy_and_paste("COORD",image_path, self.game_path["coordinate"]) + fileManager.copyAndPaste("COORD",imagePath, self.gamePath["coordinate"]) else: - self.file_manager.copy_and_paste("OVERLAYS", image_path, self.game_path["Overlays"]) + fileManager.copyAndPaste("OVERLAYS", imagePath, self.gamePath["Overlays"]) - def logic_wrapper(self, folder_path=None): - if folder_path is None: - folder_path = self.input_path - foldername = os.path.basename(folder_path) - Logger.log_msg("FOLDER", foldername) - file_list, compressed_file_list = self.file_manager.find_all_files(folder_path) + def handle(self, request, folderPath=None): + if folderPath is None: + folderPath = self.inputPath + foldername = os.path.basename(folderPath) + logger.log_msg("FOLDER", foldername) + fileList, archiveList = fileManager.findAllFiles(folderPath) - for file in file_list: - file_extension = file[2] - match file_extension: + for file in fileList: + extension = file[2] + match extension: case ".zipmod": - self.file_manager.copy_and_paste("MODS", file, self.game_path["mods"]) + fileManager.copyAndPaste("MODS", file, self.gamePath["mods"]) case ".png": - self.resolve_png(file) + self.resolvePng(file) case _: basename = os.path.basename(file[0]) - Logger.log_error("UKNOWN", f"Cannot classify {basename}") - print("[MSG]") + logger.error("UKNOWN", f"Cannot classify {basename}") - for compressed in compressed_file_list: - extract_path = self.file_manager.extract_archive(compressed[0]) - if extract_path is not None: - self.logic_wrapper(extract_path) + for archive in archiveList: + extractPath = fileManager.extractArchive(archive[0]) + if extractPath is not None: + self.handle(extractPath) + + self.setNext(request) \ No newline at end of file diff --git a/app/modules/remove_chara.py b/app/modules/remove_chara.py index a80d5f2..792fa58 100644 --- a/app/modules/remove_chara.py +++ b/app/modules/remove_chara.py @@ -1,47 +1,48 @@ import os import codecs -from util.logger import Logger -class RemoveChara: - def __init__(self, config, file_manager): - """Initializes the Bounty module. +from app.common.file_manager import fileManager +from app.common.logger import logger +from app.modules.handler import Handler - Args: - config (Config): BAAuto Config instance - """ - self.config = config - self.file_manager = file_manager - self.game_path = self.config.game_path - self.input_path = self.config.remove_chara["InputPath"] - def resolve_png(self, image_path): - with codecs.open(image_path[0], "rb") as card: +class RemoveChara(Handler): + def __str__(self) -> str: + return "Remove Chara" + + def loadConfig(self, config): + super().configLoad(config) + self.inputPath = self.config["InputPath"] + + def resolvePng(self, imagePath): + with codecs.open(imagePath[0], "rb") as card: data = card.read() if data.find(b"KoiKatuChara") != -1: if data.find(b"KoiKatuCharaSP") != -1 or data.find(b"KoiKatuCharaSun") != -1: return - self.file_manager.find_and_remove("CHARA", image_path, self.game_path["chara"]) + fileManager.findAndRemove("CHARA", imagePath, self.gamePath["chara"]) elif data.find(b"KoiKatuClothes") != -1: - self.file_manager.find_and_remove("COORD",image_path, self.game_path["coordinate"]) + fileManager.findAndRemove("COORD",imagePath, self.gamePath["coordinate"]) else: - self.file_manager.find_and_remove("OVERLAYS", image_path, self.game_path["Overlays"]) + fileManager.findAndRemove("OVERLAYS", imagePath, self.gamePath["Overlays"]) - def logic_wrapper(self): - foldername = os.path.basename(self.input_path) - Logger.log_msg("FOLDER", foldername) - file_list, archive_list = self.file_manager.find_all_files(self.input_path) + def handle(self, request): + foldername = os.path.basename(self.inputPath) + logger.info("FOLDER", foldername) + fileList, archiveList = fileManager.findAllFiles(self.inputPath) - for file in file_list: - file_extension = file[2] - match file_extension: + for file in fileList: + extension = file[2] + match extension: case ".zipmod": - self.file_manager.find_and_remove("MODS", file, self.game_path["mods"]) + fileManager.findAndRemove("MODS", file, self.game_path["mods"]) case ".png": - self.resolve_png(file) + self.resolvePng(file) case _: pass - print("[MSG]") - + + logger.line() + self.setNext(request) \ No newline at end of file diff --git a/app/modules/request.py b/app/modules/request.py new file mode 100644 index 0000000..438da0b --- /dev/null +++ b/app/modules/request.py @@ -0,0 +1,73 @@ +import os + +from app.common.config import cfg +from app.common.logger import logger + +from app.modules.handler import Handler +from app.modules.create_backup import CreateBackup +from app.modules.fc_kks import FilterConvertKKS +from app.modules.install_chara import InstallChara +from app.modules.remove_chara import RemoveChara + + +class Request: + def __init__(self): + self._handlers = [CreateBackup(), FilterConvertKKS(), InstallChara(), RemoveChara()] + self._config = cfg.toDict() + self._isValid = True + + logger.info("SCRIPT", "Validating config") + self.validateGamepath() + self._handlers = [x for x in self.handlers if self.isTaskEnabled(x)] + if not self._isValid: + raise Exception() + + def validatePath(self, path, errorMsg): + if not os.path.exists(path): + logger.error("SCRIPT", errorMsg) + self._isValid = False + return False + return True + + def validateGamepath(self): + base = self.config['Core']['GamePath'] + self.config['Core']['GamePath'] = { + "base": base, + "UserData": os.path.join(base, "UserData"), + "BepInEx": os.path.join(base, "BepInEx"), + "mods": os.path.join(base, "mods"), + "chara": os.path.join(base, "UserData\\chara\\female"), + "coordinate": os.path.join(base, "UserData\\coordinate"), + "Overlays": os.path.join(base, "UserData\\Overlays") + } + + for directory, path in self.config['Core']['GamePath'].items(): + self.validatePath(path, f"Game path not valid. Missing {directory} directory.") + + def isTaskEnabled(self, handler: Handler): + task = str(handler).replace(" ", "") + taskConfig = self.config[task] + + if not taskConfig["Enable"]: + return False + + if (path := taskConfig.get("InputPath")): + self.validatePath(path, f"Invalid path for {str(handler)}: {path}") + + if (path := taskConfig.get("OutputPath")): + self.validatePath(path, f"Invalid path for {str(handler)}: {path}") + + if self._isValid: + handler.loadConfig(self.config) + + return True + + def removeHandler(self) -> Handler: + if len(self._handlers) > 1: + return self._handlers.pop(0) + + def process(self): + if self._handlers: + self._handlers[0].handle() + + diff --git a/app/view/logger_interface.py b/app/view/logger_interface.py index c6a67a0..2f721f0 100644 --- a/app/view/logger_interface.py +++ b/app/view/logger_interface.py @@ -15,7 +15,7 @@ class LoggerInterface(QFrame): # setting label self.mainLayout = QVBoxLayout() self.topLayout = QHBoxLayout() - self.settingLabel = QLabel(self.tr("Log")) + self.loggerLabel = QLabel(self.tr("Log")) self.clearBUtton = PushButton('Clear') self.autoscrollButton = PushButton('Autoscroll Off') self.loggerBox = TextEdit() @@ -28,7 +28,7 @@ class LoggerInterface(QFrame): self.setObjectName('loggerInterface') # initialize style sheet - self.settingLabel.setObjectName('settingLabel') + self.loggerLabel.setObjectName('settingLabel') StyleSheet.SETTING_INTERFACE.apply(self) # initialize layout @@ -36,7 +36,7 @@ class LoggerInterface(QFrame): self.__connectSignalToSlot() def __initLayout(self): - self.topLayout.addWidget(self.settingLabel, alignment=Qt.AlignmentFlag.AlignLeft) + self.topLayout.addWidget(self.loggerLabel, alignment=Qt.AlignmentFlag.AlignLeft) self.topLayout.addWidget(self.clearBUtton, alignment=Qt.AlignmentFlag.AlignRight) self.topLayout.addWidget(self.autoscrollButton, alignment=Qt.AlignmentFlag.AlignRight) diff --git a/app/view/main_window.py b/app/view/main_window.py index 82c8ef8..0974ae0 100644 --- a/app/view/main_window.py +++ b/app/view/main_window.py @@ -53,10 +53,10 @@ class MainWindow(FluentWindow): ) self.navigationInterface.panel.topLayout.setAlignment(Qt.AlignCenter) scrollLayout = self.navigationInterface.panel.scrollLayout - scrollLayout.addWidget(NavigationCheckBox('Create Backup', self.settingInterface.backupGroup)) - scrollLayout.addWidget(NavigationCheckBox('Filter and Convert KKS', self.settingInterface.fckksGroup)) - scrollLayout.addWidget(NavigationCheckBox('Install Chara', self.settingInterface.installGroup)) - scrollLayout.addWidget(NavigationCheckBox('Remove Chara', self.settingInterface.removeGroup)) + scrollLayout.addWidget(NavigationCheckBox('Create Backup', cfg.backupEnable, self.settingInterface.backupGroup)) + scrollLayout.addWidget(NavigationCheckBox('Filter and Convert KKS', cfg.fckksEnable, self.settingInterface.fckksGroup)) + scrollLayout.addWidget(NavigationCheckBox('Install Chara', cfg.installEnable, self.settingInterface.installGroup)) + scrollLayout.addWidget(NavigationCheckBox('Remove Chara', cfg.removeEnable, self.settingInterface.removeGroup)) scrollLayout.addWidget(NavigationActionButtons()) # add custom widget to bottom diff --git a/script.py b/script.py index b3412d6..3ac79a6 100644 --- a/script.py +++ b/script.py @@ -4,7 +4,7 @@ try: with open('traceback.log', 'w') as f: pass - from app.common.logger import Logger + from app.common.logger import logger from app.modules.install_chara import InstallChara from app.modules.remove_chara import RemoveChara from app.modules.fc_kks import FilterConvertKKS @@ -20,7 +20,7 @@ try: config (Config): BAAuto Config instance """ self.config = config - self.file_manager = file_manager + fileManager = file_manager self.modules = { 'InstallChara': None, 'RemoveChara': None, @@ -28,22 +28,22 @@ try: 'FCKKS': None, } if self.config.install_chara['Enable']: - self.modules['InstallChara'] = InstallChara(self.config, self.file_manager) + self.modules['InstallChara'] = InstallChara(self.config, fileManager) if self.config.remove_chara['Enable']: - self.modules['RemoveChara'] = RemoveChara(self.config, self.file_manager) + self.modules['RemoveChara'] = RemoveChara(self.config, fileManager) if self.config.create_backup['Enable']: - self.modules['CreateBackup'] = CreateBackup(self.config, self.file_manager) + self.modules['CreateBackup'] = CreateBackup(self.config, fileManager) if self.config.fc_kks["Enable"]: - self.modules['FCKKS'] = FilterConvertKKS(self.config, self.file_manager) + self.modules['FCKKS'] = FilterConvertKKS(self.config, fileManager) def run(self): for task in self.config.tasks: if self.modules[task]: - Logger.log_info("SCRIPT", f'Start Task: {task}') + logger.info("SCRIPT", f'Start Task: {task}') try: self.modules[task].logic_wrapper() except: - Logger.log_error("SCRIPT", f'Task error: {task}. For more info, check the traceback.log file.') + logger.error("SCRIPT", f'Task error: {task}. For more info, check the traceback.log file.') with open('traceback.log', 'a') as f: f.write(f'[{task}]\n') traceback.print_exc(None, f, True)