mirror of
https://github.com/RedDeadDepresso/KKAFIO.git
synced 2025-12-22 01:10:01 +00:00
refactor: use pathlib instead of os.path
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
from PySide6.QtCore import QThread, QProcess, Signal, QObject
|
|
||||||
import os
|
import os
|
||||||
|
from PySide6.QtCore import QProcess, Signal, QObject
|
||||||
|
|
||||||
|
|
||||||
class ScriptManager(QObject):
|
class ScriptManager(QObject):
|
||||||
def __init__(self, signalBus):
|
def __init__(self, signalBus):
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
class CreateBackup:
|
class CreateBackup:
|
||||||
def __init__(self, config, file_manager):
|
def __init__(self, config, file_manager):
|
||||||
"""Initializes the Bounty module.
|
"""Initializes the CreateBackup module.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config (Config): BAAuto Config instance
|
config (Config): KKAFIO Config instance
|
||||||
"""
|
"""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.file_manager = file_manager
|
self.file_manager = file_manager
|
||||||
@@ -15,8 +13,8 @@ class CreateBackup:
|
|||||||
self.filename = self.config.create_backup["Filename"]
|
self.filename = self.config.create_backup["Filename"]
|
||||||
self.output_path = self.config.create_backup["OutputPath"]
|
self.output_path = self.config.create_backup["OutputPath"]
|
||||||
|
|
||||||
def logic_wrapper(self):
|
def run(self):
|
||||||
output_path = os.path.join(self.output_path, self.filename)
|
output_path = self.output_path / self.filename
|
||||||
self.file_manager.create_archive(self.folders, output_path)
|
self.file_manager.create_archive(self.folders, output_path)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +1,43 @@
|
|||||||
import os
|
|
||||||
import re as regex
|
|
||||||
import codecs
|
|
||||||
import shutil
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
from util.logger import logger
|
from util.logger import logger
|
||||||
|
|
||||||
|
|
||||||
class FilterConvertKKS:
|
class FilterConvertKKS:
|
||||||
def __init__(self, config, file_manager):
|
def __init__(self, config, file_manager):
|
||||||
"""Initializes the Bounty module.
|
"""Initializes the FilterConvertKKS module.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config (Config): BAAuto Config instance
|
config (Config): KKAFIO Config instance
|
||||||
"""
|
"""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.file_manager = file_manager
|
self.file_manager = file_manager
|
||||||
self.convert = self.config.fc_kks["Convert"]
|
self.convert = self.config.fc_kks["Convert"]
|
||||||
|
|
||||||
def get_list(self, folder_path):
|
def get_list(self, folder_path):
|
||||||
new_list = []
|
"""Get list of PNG files in the folder."""
|
||||||
for root, dirs, files in os.walk(folder_path):
|
folder = Path(folder_path)
|
||||||
for filename in files:
|
return [str(file) for file in folder.rglob("*.png")]
|
||||||
if regex.match(r".*(\.png)$", filename):
|
|
||||||
new_list.append(os.path.join(root, filename))
|
|
||||||
return new_list
|
|
||||||
|
|
||||||
def check_png(self, card_path):
|
def check_png(self, card_path):
|
||||||
with codecs.open(card_path, "rb") as card:
|
"""Check the PNG file and return its type."""
|
||||||
|
card_path = Path(card_path)
|
||||||
|
with card_path.open("rb") as card:
|
||||||
data = card.read()
|
data = card.read()
|
||||||
card_type = 0
|
card_type = 0
|
||||||
if data.find(b"KoiKatuChara") != -1:
|
if b"KoiKatuChara" in data:
|
||||||
card_type = 1
|
card_type = 1
|
||||||
if data.find(b"KoiKatuCharaSP") != -1:
|
if b"KoiKatuCharaSP" in data:
|
||||||
card_type = 2
|
card_type = 2
|
||||||
elif data.find(b"KoiKatuCharaSun") != -1:
|
elif b"KoiKatuCharaSun" in data:
|
||||||
card_type = 3
|
card_type = 3
|
||||||
logger.info(f"[{card_type}]", f"{card_path}")
|
logger.info(f"[{card_type}]", f"{card_path}")
|
||||||
return card_type
|
return card_type
|
||||||
|
|
||||||
def convert_kk(self, card_name, card_path, destination_path):
|
def convert_kk(self, card_name, card_path, destination_path):
|
||||||
with codecs.open(card_path, mode="rb") as card:
|
"""Convert KKS card to KK."""
|
||||||
|
card_path = Path(card_path) # Convert to Path object
|
||||||
|
with card_path.open(mode="rb") as card:
|
||||||
data = card.read()
|
data = card.read()
|
||||||
|
|
||||||
replace_list = [
|
replace_list = [
|
||||||
@@ -46,20 +46,20 @@ class FilterConvertKKS:
|
|||||||
[b"version\xa50.0.6\xa3sex", b"version\xa50.0.5\xa3sex"],
|
[b"version\xa50.0.6\xa3sex", b"version\xa50.0.5\xa3sex"],
|
||||||
]
|
]
|
||||||
|
|
||||||
for text in replace_list:
|
for old_text, new_text in replace_list:
|
||||||
data = data.replace(text[0], text[1])
|
data = data.replace(old_text, new_text)
|
||||||
|
|
||||||
new_file_path = os.path.normpath(os.path.join(destination_path, f"KKS2KK_{card_name}"))
|
new_file_path = Path(destination_path) / f"KKS2KK_{card_name}"
|
||||||
# print(f"new_file_path {new_file_path}")
|
|
||||||
|
|
||||||
with codecs.open(new_file_path, "wb") as new_card:
|
with new_file_path.open("wb") as new_card:
|
||||||
new_card.write(data)
|
new_card.write(data)
|
||||||
|
|
||||||
def logic_wrapper(self):
|
def run(self):
|
||||||
path = self.config.fc_kks["InputPath"]
|
"""Main logic for processing the KKS to KK conversion."""
|
||||||
|
path = Path(self.config.fc_kks["InputPath"])
|
||||||
kks_card_list = []
|
kks_card_list = []
|
||||||
kks_folder = "_KKS_card_"
|
kks_folder = path / "_KKS_card_"
|
||||||
kks_folder2 = "_KKS_to_KK_"
|
kks_folder2 = path / "_KKS_to_KK_"
|
||||||
|
|
||||||
png_list = self.get_list(path)
|
png_list = self.get_list(path)
|
||||||
|
|
||||||
@@ -70,38 +70,34 @@ class FilterConvertKKS:
|
|||||||
if self.check_png(png) == 3:
|
if self.check_png(png) == 3:
|
||||||
kks_card_list.append(png)
|
kks_card_list.append(png)
|
||||||
else:
|
else:
|
||||||
logger.success("SCRIPT", f"no PNG found")
|
logger.success("SCRIPT", "No PNG files found")
|
||||||
return
|
return
|
||||||
|
|
||||||
count = len(kks_card_list)
|
count = len(kks_card_list)
|
||||||
if count > 0:
|
if count > 0:
|
||||||
print(kks_card_list)
|
print(kks_card_list)
|
||||||
|
|
||||||
target_folder = os.path.normpath(os.path.join(path, kks_folder))
|
# Create target directories if they don't exist
|
||||||
target_folder2 = os.path.normpath(os.path.join(path, kks_folder2))
|
kks_folder.mkdir(exist_ok=True)
|
||||||
if not os.path.isdir(target_folder):
|
|
||||||
os.mkdir(target_folder)
|
|
||||||
|
|
||||||
if self.convert:
|
if self.convert:
|
||||||
logger.info("SCRIPT", f"Conversion to KK is [{self.convert}]")
|
logger.info("SCRIPT", f"Conversion to KK is [{self.convert}]")
|
||||||
if not os.path.isdir(target_folder2):
|
kks_folder2.mkdir(exist_ok=True)
|
||||||
os.mkdir(target_folder2)
|
|
||||||
|
|
||||||
for card_path in kks_card_list:
|
for card_path in kks_card_list:
|
||||||
source = card_path
|
source = Path(card_path)
|
||||||
card = os.path.basename(card_path)
|
target = kks_folder / source.name
|
||||||
target = os.path.normpath(os.path.join(target_folder, card))
|
|
||||||
|
|
||||||
# copy & convert before move
|
# Copy & convert before moving
|
||||||
if self.convert:
|
if self.convert:
|
||||||
self.convert_kk(card, source, target_folder2)
|
self.convert_kk(source.name, source, kks_folder2)
|
||||||
|
|
||||||
# move file
|
# Move file
|
||||||
shutil.move(source, target)
|
shutil.move(str(source), str(target))
|
||||||
|
|
||||||
if self.convert:
|
if self.convert:
|
||||||
logger.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 [{kks_folder}] folder, converted and saved to [{kks_folder2}] folder")
|
||||||
else:
|
else:
|
||||||
logger.success("SCRIPT", f"[{count}] cards moved to [{kks_folder}] folder")
|
logger.success("SCRIPT", f"[{count}] cards moved to [{kks_folder}] folder")
|
||||||
else:
|
else:
|
||||||
logger.success("SCRIPT", f"no KKS card found")
|
logger.success("SCRIPT: No KKS cards found")
|
||||||
|
|||||||
@@ -1,40 +1,43 @@
|
|||||||
import os
|
from pathlib import Path
|
||||||
import codecs
|
import codecs
|
||||||
from util.logger import logger
|
from util.logger import logger
|
||||||
|
|
||||||
|
|
||||||
class InstallChara:
|
class InstallChara:
|
||||||
def __init__(self, config, file_manager):
|
def __init__(self, config, file_manager):
|
||||||
"""Initializes the Bounty module.
|
"""Initializes the InstallChara module.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config (Config): BAAuto Config instance
|
config (Config): KKAFIO Config instance
|
||||||
"""
|
"""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.file_manager = file_manager
|
self.file_manager = file_manager
|
||||||
self.game_path = self.config.game_path
|
self.game_path = self.config.game_path
|
||||||
self.input_path = self.config.install_chara["InputPath"]
|
self.input_path = Path(self.config.install_chara["InputPath"]) # Using Path for input path
|
||||||
|
|
||||||
def resolve_png(self, image_path):
|
def resolve_png(self, image_path):
|
||||||
with codecs.open(image_path[0], "rb") as card:
|
with codecs.open(image_path[0], "rb") as card:
|
||||||
data = card.read()
|
data = card.read()
|
||||||
if data.find(b"KoiKatuChara") != -1:
|
if b"KoiKatuChara" in data:
|
||||||
if data.find(b"KoiKatuCharaSP") != -1 or data.find(b"KoiKatuCharaSun") != -1:
|
if b"KoiKatuCharaSP" in data or b"KoiKatuCharaSun" in data:
|
||||||
basename = os.path.basename(image_path[0])
|
basename = Path(image_path[0]).name # Use Path's .name to get the basename
|
||||||
logger.error("CHARA", f"{basename} is a KKS card")
|
logger.error("CHARA", f"{basename} is a KKS card")
|
||||||
return
|
return
|
||||||
self.file_manager.copy_and_paste("CHARA", image_path, self.game_path["chara"])
|
self.file_manager.copy_and_paste("CHARA", image_path, self.game_path["chara"])
|
||||||
elif data.find(b"KoiKatuClothes") != -1:
|
elif b"KoiKatuClothes" in data:
|
||||||
self.file_manager.copy_and_paste("COORD",image_path, self.game_path["coordinate"])
|
self.file_manager.copy_and_paste("COORD", image_path, self.game_path["coordinate"])
|
||||||
else:
|
else:
|
||||||
self.file_manager.copy_and_paste("OVERLAYS", image_path, self.game_path["Overlays"])
|
self.file_manager.copy_and_paste("OVERLAYS", image_path, self.game_path["Overlays"])
|
||||||
|
|
||||||
def logic_wrapper(self, folder_path=None):
|
def run(self, folder_path=None):
|
||||||
if folder_path is None:
|
if folder_path is None:
|
||||||
folder_path = self.input_path
|
folder_path = self.input_path
|
||||||
foldername = os.path.basename(folder_path)
|
folder_path = Path(folder_path)
|
||||||
|
foldername = folder_path.name
|
||||||
logger.line()
|
logger.line()
|
||||||
logger.info("FOLDER", foldername)
|
logger.info("FOLDER", foldername)
|
||||||
file_list, compressed_file_list = self.file_manager.find_all_files(folder_path)
|
|
||||||
|
file_list, archive_list = self.file_manager.find_all_files(folder_path)
|
||||||
|
|
||||||
for file in file_list:
|
for file in file_list:
|
||||||
file_extension = file[2]
|
file_extension = file[2]
|
||||||
@@ -44,14 +47,11 @@ class InstallChara:
|
|||||||
case ".png":
|
case ".png":
|
||||||
self.resolve_png(file)
|
self.resolve_png(file)
|
||||||
case _:
|
case _:
|
||||||
basename = os.path.basename(file[0])
|
basename = Path(file[0]).name
|
||||||
logger.error("UKNOWN", f"Cannot classify {basename}")
|
logger.error("UNKNOWN", f"Cannot classify {basename}")
|
||||||
logger.line()
|
logger.line()
|
||||||
|
|
||||||
for compressed in compressed_file_list:
|
for archive in archive_list:
|
||||||
extract_path = self.file_manager.extract_archive(compressed[0])
|
extract_path = self.file_manager.extract_archive(archive[0])
|
||||||
if extract_path is not None:
|
if extract_path is not None:
|
||||||
self.logic_wrapper(extract_path)
|
self.run(extract_path)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import os
|
|
||||||
import codecs
|
import codecs
|
||||||
from util.logger import logger
|
from util.logger import logger
|
||||||
|
|
||||||
|
|
||||||
class RemoveChara:
|
class RemoveChara:
|
||||||
def __init__(self, config, file_manager):
|
def __init__(self, config, file_manager):
|
||||||
"""Initializes the Bounty module.
|
"""Initializes the RemoveChara module.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config (Config): BAAuto Config instance
|
config (Config): KKAFIO Config instance
|
||||||
"""
|
"""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.file_manager = file_manager
|
self.file_manager = file_manager
|
||||||
@@ -18,23 +17,24 @@ class RemoveChara:
|
|||||||
def resolve_png(self, image_path):
|
def resolve_png(self, image_path):
|
||||||
with codecs.open(image_path[0], "rb") as card:
|
with codecs.open(image_path[0], "rb") as card:
|
||||||
data = card.read()
|
data = card.read()
|
||||||
if data.find(b"KoiKatuChara") != -1:
|
if b"KoiKatuChara" in data:
|
||||||
if data.find(b"KoiKatuCharaSP") != -1 or data.find(b"KoiKatuCharaSun") != -1:
|
if b"KoiKatuCharaSP" in data or b"KoiKatuCharaSun" in data:
|
||||||
return
|
return
|
||||||
self.file_manager.find_and_remove("CHARA", image_path, self.game_path["chara"])
|
self.file_manager.find_and_remove("CHARA", image_path, self.game_path["chara"])
|
||||||
elif data.find(b"KoiKatuClothes") != -1:
|
elif b"KoiKatuClothes" in data:
|
||||||
self.file_manager.find_and_remove("COORD",image_path, self.game_path["coordinate"])
|
self.file_manager.find_and_remove("COORD", image_path, self.game_path["coordinate"])
|
||||||
else:
|
else:
|
||||||
self.file_manager.find_and_remove("OVERLAYS", image_path, self.game_path["Overlays"])
|
self.file_manager.find_and_remove("OVERLAYS", image_path, self.game_path["Overlays"])
|
||||||
|
|
||||||
def logic_wrapper(self):
|
def run(self):
|
||||||
foldername = os.path.basename(self.input_path)
|
foldername = self.input_path.name
|
||||||
logger.info("FOLDER", foldername)
|
logger.info("FOLDER", foldername)
|
||||||
|
|
||||||
file_list, archive_list = self.file_manager.find_all_files(self.input_path)
|
file_list, archive_list = self.file_manager.find_all_files(self.input_path)
|
||||||
|
|
||||||
for file in file_list:
|
for file in file_list:
|
||||||
file_extension = file[2]
|
extension = file[2]
|
||||||
match file_extension:
|
match extension:
|
||||||
case ".zipmod":
|
case ".zipmod":
|
||||||
self.file_manager.find_and_remove("MODS", file, self.game_path["mods"])
|
self.file_manager.find_and_remove("MODS", file, self.game_path["mods"])
|
||||||
case ".png":
|
case ".png":
|
||||||
@@ -42,7 +42,3 @@ class RemoveChara:
|
|||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
logger.line()
|
logger.line()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
50
script.py
50
script.py
@@ -21,39 +21,35 @@ try:
|
|||||||
Args:
|
Args:
|
||||||
config (Config): BAAuto Config instance
|
config (Config): BAAuto Config instance
|
||||||
"""
|
"""
|
||||||
logger.logger_signal = None
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.file_manager = file_manager
|
self.file_manager = file_manager
|
||||||
self.modules = {
|
self.task_to_module = {
|
||||||
'InstallChara': None,
|
'CreateBackup': CreateBackup,
|
||||||
'RemoveChara': None,
|
'FilterConvertKKS': FilterConvertKKS,
|
||||||
'CreateBackup': None,
|
'InstallChara': InstallChara,
|
||||||
'FilterConvertKKS': None,
|
'RemoveChara': RemoveChara,
|
||||||
}
|
}
|
||||||
if self.config.install_chara['Enable']:
|
|
||||||
self.modules['InstallChara'] = InstallChara(self.config, self.file_manager)
|
|
||||||
if self.config.remove_chara['Enable']:
|
|
||||||
self.modules['RemoveChara'] = RemoveChara(self.config, self.file_manager)
|
|
||||||
if self.config.create_backup['Enable']:
|
|
||||||
self.modules['CreateBackup'] = CreateBackup(self.config, self.file_manager)
|
|
||||||
if self.config.fc_kks["Enable"]:
|
|
||||||
self.modules['FilterConvertKKS'] = FilterConvertKKS(self.config, self.file_manager)
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
for task in self.config.tasks:
|
for task, module in self.task_to_module.items():
|
||||||
if self.modules[task]:
|
if not self.config.config_data[task]["Enable"]:
|
||||||
logger.info("SCRIPT", f'Start Task: {task}')
|
continue
|
||||||
try:
|
|
||||||
self.modules[task].logic_wrapper()
|
logger.info("SCRIPT", f'Start Task: {task}')
|
||||||
except:
|
try:
|
||||||
logger.error("SCRIPT", f'Task error: {task}. For more info, check the traceback.log file.')
|
module(self.config, self.file_manager).run()
|
||||||
with open('traceback.log', 'a') as f:
|
except:
|
||||||
f.write(f'[{task}]\n')
|
logger.error("SCRIPT", f'Task error: {task}. For more info, check the traceback.log file.')
|
||||||
traceback.print_exc(None, f, True)
|
self.write_traceback(task)
|
||||||
f.write('\n')
|
sys.exit(1)
|
||||||
sys.exit(1)
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
def write_traceback(self, task):
|
||||||
|
with open('traceback.log', 'a') as f:
|
||||||
|
f.write(f'[{task}]\n')
|
||||||
|
traceback.print_exc(None, f, True)
|
||||||
|
f.write('\n')
|
||||||
|
|
||||||
except:
|
except:
|
||||||
print(f'[ERROR] Script Initialisation Error. For more info, check the traceback.log file.')
|
print(f'[ERROR] Script Initialisation Error. For more info, check the traceback.log file.')
|
||||||
with open('traceback.log', 'w') as f:
|
with open('traceback.log', 'w') as f:
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import os
|
from pathlib import Path
|
||||||
from util.logger import logger
|
from util.logger import logger
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, config_file):
|
def __init__(self, config_file):
|
||||||
logger.info("SCRIPT", "Initializing config module")
|
logger.info("SCRIPT", "Initializing config module")
|
||||||
@@ -10,15 +11,12 @@ class Config:
|
|||||||
self.ok = False
|
self.ok = False
|
||||||
self.initialized = False
|
self.initialized = False
|
||||||
self.config_data = None
|
self.config_data = None
|
||||||
self.changed = False
|
|
||||||
self.read()
|
self.read()
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
backup_config = self._deepcopy_dict(self.__dict__)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(self.config_file, 'r') as json_file:
|
with open(self.config_file, 'r') as f:
|
||||||
self.config_data = json.load(json_file)
|
self.config_data = json.load(f)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logger.error("SCRIPT", f"Config file '{self.config_file}' not found.")
|
logger.error("SCRIPT", f"Config file '{self.config_file}' not found.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -31,60 +29,54 @@ class Config:
|
|||||||
if self.ok and not self.initialized:
|
if self.ok and not self.initialized:
|
||||||
logger.info("SCRIPT", "Starting KKAFIO!")
|
logger.info("SCRIPT", "Starting KKAFIO!")
|
||||||
self.initialized = True
|
self.initialized = True
|
||||||
self.changed = True
|
|
||||||
elif not self.ok and not self.initialized:
|
elif not self.ok and not self.initialized:
|
||||||
logger.error("SCRIPT", "Invalid config. Please check your config file.")
|
logger.error("SCRIPT", "Invalid config. Please check your config file.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
elif not self.ok and self.initialized:
|
|
||||||
logger.warning("SCRIPT", "Config change detected, but with problems. Rolling back config.")
|
|
||||||
self._rollback_config(backup_config)
|
|
||||||
elif self.ok and self.initialized:
|
|
||||||
if backup_config != self.__dict__:
|
|
||||||
logger.warning("SCRIPT", "Config change detected. Hot-reloading.")
|
|
||||||
self.changed = True
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
logger.info("SCRIPT", "Validating config")
|
logger.info("SCRIPT", "Validating config")
|
||||||
self.ok = True
|
self.ok = True
|
||||||
self.tasks = ["CreateBackup", "FilterConvertKKS", "InstallChara", "RemoveChara"]
|
self.validate_gamepath()
|
||||||
self.create_gamepath()
|
self.validate_tasks()
|
||||||
|
|
||||||
for task in self.tasks:
|
def validate_gamepath(self):
|
||||||
if self.config_data[task]["Enable"]:
|
base = Path(self.config_data["Core"]["GamePath"])
|
||||||
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.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("FilterConvertKKS", {})
|
|
||||||
|
|
||||||
def create_gamepath(self):
|
|
||||||
base = self.config_data["Core"]["GamePath"]
|
|
||||||
self.game_path = {
|
self.game_path = {
|
||||||
"base": base,
|
"base": base,
|
||||||
"UserData": os.path.join(base, "UserData"),
|
"UserData": base / "UserData",
|
||||||
"BepInEx": os.path.join(base, "BepInEx"),
|
"BepInEx": base / "BepInEx",
|
||||||
"mods": os.path.join(base, "mods"),
|
"mods": base / "mods",
|
||||||
"chara": os.path.join(base, "UserData\\chara\\female"),
|
"chara": base / "UserData" / "chara" / "female",
|
||||||
"coordinate": os.path.join(base, "UserData\\coordinate"),
|
"coordinate": base / "UserData" / "coordinate",
|
||||||
"Overlays": os.path.join(base, "UserData\\Overlays")
|
"Overlays": base / "UserData" / "Overlays"
|
||||||
}
|
}
|
||||||
|
|
||||||
for path in self.game_path.values():
|
for path in self.game_path.values():
|
||||||
if not os.path.exists(path):
|
if not path.exists():
|
||||||
logger.error("SCRIPT", "Game path not valid")
|
logger.error("SCRIPT", "Game path not valid")
|
||||||
raise Exception()
|
raise Exception(f"Game path not valid: {path}")
|
||||||
|
|
||||||
def _deepcopy_dict(self, dictionary):
|
|
||||||
from copy import deepcopy
|
|
||||||
return deepcopy(dictionary)
|
|
||||||
|
|
||||||
def _rollback_config(self, config):
|
def validate_tasks(self):
|
||||||
for key, value in config.items():
|
tasks = ["CreateBackup", "FilterConvertKKS", "InstallChara", "RemoveChara"]
|
||||||
setattr(self, key, value)
|
|
||||||
|
for task in tasks:
|
||||||
|
task_config = self.config_data[task]
|
||||||
|
if not task_config["Enable"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "InputPath" in task_config:
|
||||||
|
path_obj = Path(task_config["InputPath"])
|
||||||
|
task_config["InputPath"] = path_obj
|
||||||
|
|
||||||
|
elif "OutputPath" in task_config:
|
||||||
|
path_obj = Path(task_config["OutputPath"])
|
||||||
|
task_config["OutputPath"] = path_obj
|
||||||
|
|
||||||
|
if not path_obj.exists():
|
||||||
|
logger.error("SCRIPT", f"Path invalid for task {task}")
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
self.create_backup = self.config_data["CreateBackup"]
|
||||||
|
self.fc_kks = self.config_data["FilterConvertKKS"]
|
||||||
|
self.install_chara = self.config_data["InstallChara"]
|
||||||
|
self.remove_chara = self.config_data["RemoveChara"]
|
||||||
|
|||||||
@@ -1,44 +1,47 @@
|
|||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
import datetime
|
import datetime
|
||||||
import patoolib
|
import patoolib
|
||||||
import customtkinter
|
import customtkinter
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
from pathlib import Path
|
||||||
from util.logger import logger
|
from util.logger import logger
|
||||||
|
|
||||||
|
|
||||||
class FileManager:
|
class FileManager:
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def find_all_files(self, directory):
|
def find_all_files(self, directory):
|
||||||
|
"""Find all files in the given directory."""
|
||||||
|
directory = Path(directory)
|
||||||
file_list = []
|
file_list = []
|
||||||
compressed_file_list = []
|
archive_list = []
|
||||||
compressed_extensions = [".rar", ".zip", ".7z"]
|
archive_extensions = {".rar", ".zip", ".7z"}
|
||||||
|
|
||||||
for root, dirs, files in os.walk(directory):
|
for file_path in directory.rglob('*'):
|
||||||
for file in files:
|
file_size = file_path.stat().st_size
|
||||||
|
file_extension = file_path.suffix
|
||||||
|
|
||||||
file_path = os.path.join(root, file)
|
if file_extension in archive_extensions:
|
||||||
file_size = os.path.getsize(file_path)
|
archive_list.append((file_path, file_size, file_extension))
|
||||||
_, file_extension = os.path.splitext(file_path)
|
else:
|
||||||
|
file_list.append((file_path, file_size, file_extension))
|
||||||
if file_extension in compressed_extensions:
|
|
||||||
compressed_file_list.append((file_path, file_size, file_extension))
|
|
||||||
else:
|
|
||||||
file_list.append((file_path, file_size, file_extension))
|
|
||||||
|
|
||||||
file_list.sort(key=lambda x: x[1])
|
file_list.sort(key=lambda x: x[1])
|
||||||
compressed_file_list.sort(key=lambda x: x[1])
|
archive_list.sort(key=lambda x: x[1])
|
||||||
return file_list, compressed_file_list
|
return file_list, archive_list
|
||||||
|
|
||||||
def copy_and_paste(self, type, source_path, destination_folder):
|
def copy_and_paste(self, type, source_path, destination_folder):
|
||||||
source_path = source_path[0]
|
"""Copy file from source to destination, handling file conflicts."""
|
||||||
base_name = os.path.basename(source_path)
|
source_path = Path(source_path[0])
|
||||||
destination_path = os.path.join(destination_folder, base_name)
|
destination_folder = Path(destination_folder)
|
||||||
|
|
||||||
|
base_name = source_path.name
|
||||||
|
destination_path = destination_folder / base_name
|
||||||
conflicts = self.config.install_chara["FileConflicts"]
|
conflicts = self.config.install_chara["FileConflicts"]
|
||||||
already_exists = os.path.exists(destination_path)
|
already_exists = destination_path.exists()
|
||||||
|
|
||||||
if already_exists and conflicts == "Skip":
|
if already_exists and conflicts == "Skip":
|
||||||
logger.skipped(type, base_name)
|
logger.skipped(type, base_name)
|
||||||
@@ -51,26 +54,19 @@ class FileManager:
|
|||||||
max_retries = 3
|
max_retries = 3
|
||||||
for attempt in range(max_retries):
|
for attempt in range(max_retries):
|
||||||
try:
|
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.renamed(type, base_name)
|
logger.renamed(type, base_name)
|
||||||
|
new_name = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S-%f')
|
||||||
filename, file_extension = os.path.splitext(destination_path)
|
destination_path = destination_path.with_stem(f"{destination_path.stem}_{new_name}")
|
||||||
destination_path = f"{filename}_{new_name}{file_extension}"
|
break
|
||||||
break # Exit the loop if renaming is successful
|
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
if attempt < max_retries - 1:
|
if attempt < max_retries - 1:
|
||||||
time.sleep(1) # Wait for 1 second before retrying
|
time.sleep(1)
|
||||||
else:
|
else:
|
||||||
logger.error(type, f"Failed to rename {base_name} after {max_retries} attempts.")
|
logger.error(type, f"Failed to rename {base_name} after {max_retries} attempts.")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shutil.copy(source_path, destination_path)
|
shutil.copy(source_path, destination_path)
|
||||||
print(f"File copied successfully from {source_path} to {destination_path}")
|
|
||||||
if not already_exists:
|
if not already_exists:
|
||||||
logger.success(type, base_name)
|
logger.success(type, base_name)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
@@ -80,70 +76,78 @@ class FileManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.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):
|
def find_and_remove(self, type, source_path, destination_folder_path):
|
||||||
source_path = source_path[0]
|
"""Remove file if it exists at the destination."""
|
||||||
base_name = os.path.basename(source_path)
|
source_path = Path(source_path[0])
|
||||||
destination_path = os.path.join(destination_folder, base_name)
|
destination_folder_path = Path(destination_folder_path)
|
||||||
if os.path.exists(destination_path):
|
|
||||||
|
base_name = source_path.name
|
||||||
|
destination_path = destination_folder_path / base_name
|
||||||
|
|
||||||
|
if destination_path.exists():
|
||||||
try:
|
try:
|
||||||
os.remove(destination_path)
|
destination_path.unlink()
|
||||||
logger.removed(type, base_name)
|
logger.removed(type, base_name)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.error(type, base_name)
|
logger.error(type, base_name)
|
||||||
|
|
||||||
def create_archive(self, folders, archive_path):
|
def create_archive(self, folders, archive_path):
|
||||||
|
"""Create an archive of the given folders using 7zip."""
|
||||||
# Specify the full path to the 7zip executable
|
# Specify the full path to the 7zip executable
|
||||||
path_to_7zip = patoolib.util.find_program("7z")
|
path_to_7zip = patoolib.util.find_program("7z")
|
||||||
if not path_to_7zip:
|
if not path_to_7zip:
|
||||||
logger.error("SCRIPT", "7zip not found. Unable to create backup")
|
logger.error("SCRIPT", "7zip not found. Unable to create backup")
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
if os.path.exists(archive_path+".7z"):
|
archive_path = Path(archive_path)
|
||||||
os.remove(archive_path+".7z")
|
archive_file = archive_path.with_suffix(".7z")
|
||||||
|
|
||||||
path_to_7zip = f'"{path_to_7zip}"'
|
if archive_file.exists():
|
||||||
archive_path = f'"{archive_path}"'
|
archive_file.unlink()
|
||||||
|
|
||||||
exclude_folders = [
|
exclude_folders = [
|
||||||
'"Sideloader Modpack"',
|
"Sideloader Modpack",
|
||||||
'"Sideloader Modpack - Studio"',
|
"Sideloader Modpack - Studio",
|
||||||
'"Sideloader Modpack - KK_UncensorSelector"',
|
"Sideloader Modpack - KK_UncensorSelector",
|
||||||
'"Sideloader Modpack - Maps"',
|
"Sideloader Modpack - Maps",
|
||||||
'"Sideloader Modpack - KK_MaterialEditor"',
|
"Sideloader Modpack - KK_MaterialEditor",
|
||||||
'"Sideloader Modpack - Fixes"',
|
"Sideloader Modpack - Fixes",
|
||||||
'"Sideloader Modpack - Exclusive KK KKS"',
|
"Sideloader Modpack - Exclusive KK KKS",
|
||||||
'"Sideloader Modpack - Exclusive KK"',
|
"Sideloader Modpack - Exclusive KK",
|
||||||
'"Sideloader Modpack - Animations"'
|
"Sideloader Modpack - Animations"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Create a string of folder names to exclude
|
# Create a string of folder names to exclude
|
||||||
exclude_string = ''
|
exclude_string = ' '.join([f'-xr!"{folder}"' for folder in exclude_folders])
|
||||||
for folder in exclude_folders:
|
|
||||||
exclude_string += f'-xr!{folder} '
|
|
||||||
|
|
||||||
# Create a string of folder names to include
|
# Create a string of folder names to include
|
||||||
include_string = ''
|
include_string = ' '.join([f'"{folder}"' for folder in folders])
|
||||||
for folder in folders:
|
|
||||||
include_string += f'"{folder}" '
|
|
||||||
|
|
||||||
# Construct the 7zip command
|
# Construct the 7zip command
|
||||||
command = f'{path_to_7zip} a -t7z {archive_path} {include_string} {exclude_string}'
|
command = f'"{path_to_7zip}" a -t7z "{archive_file}" {include_string} {exclude_string}'
|
||||||
|
|
||||||
# Call the command
|
# Call the command
|
||||||
process = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
process = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
|
||||||
# Print the output
|
# Print the output
|
||||||
for line in process.stdout.decode().split('\n'):
|
for line in process.stdout.decode().split('\n'):
|
||||||
if line.strip() != "":
|
if line.strip():
|
||||||
logger.info("7-Zip", line)
|
logger.info("7-Zip", line)
|
||||||
|
|
||||||
# Check the return code
|
# Check the return code
|
||||||
if process.returncode not in [0, 1]:
|
if process.returncode not in [0, 1]:
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
def extract_archive(self, archive_path):
|
def extract_archive(self, archive_path):
|
||||||
|
"""Extract the archive."""
|
||||||
|
archive_path = Path(archive_path)
|
||||||
|
archive_name = archive_path.name
|
||||||
|
logger.info("ARCHIVE", f"Extracting {archive_name}")
|
||||||
|
|
||||||
|
extract_path = archive_path.with_stem(f"{archive_path.stem}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
archive_name = os.path.basename(archive_path)
|
patoolib.extract_archive(str(archive_path), outdir=str(extract_path))
|
||||||
logger.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
|
return extract_path
|
||||||
|
|
||||||
except patoolib.util.PatoolError as e:
|
except patoolib.util.PatoolError as e:
|
||||||
@@ -153,8 +157,8 @@ class FileManager:
|
|||||||
dialog = customtkinter.CTkInputDialog(text=text, title="Enter Password")
|
dialog = customtkinter.CTkInputDialog(text=text, title="Enter Password")
|
||||||
password = dialog.get_input()
|
password = dialog.get_input()
|
||||||
|
|
||||||
if password is not None or "":
|
if password:
|
||||||
patoolib.extract_archive(archive_path, outdir=extract_path, password=password)
|
patoolib.extract_archive(str(archive_path), outdir=str(extract_path), password=password)
|
||||||
return extract_path
|
return extract_path
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
@@ -162,4 +166,3 @@ class FileManager:
|
|||||||
text = f"Wrong password or {archive_name} is corrupted. Please enter password again or click Cancel"
|
text = f"Wrong password or {archive_name} is corrupted. Please enter password again or click Cancel"
|
||||||
|
|
||||||
logger.skipped("ARCHIVE", archive_name)
|
logger.skipped("ARCHIVE", archive_name)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user