from pathlib import Path from zipfile import Path as ZipPath from zipfile import ZipFile, is_zipfile from leek.utils import GameFinder class ModInstaller: __archive_path: Path __archive_format: str __is_flat: bool __is_installed: bool def __init__(self, mod_path: Path) -> None: self.__archive_path = mod_path self.__is_installed = False if is_zipfile(mod_path): self.__archive_format = "zip" archive: ZipFile = ZipFile(mod_path) path: ZipPath = ZipPath(archive) # Make sure archive contains a mod things_in_root: list[ZipPath] = list(path.iterdir()) config_toml: ZipPath match len(things_in_root): case 0: raise EmptyArchiveError case 1: config_toml = things_in_root[0] / "config.toml" if config_toml.exists() and config_toml.is_file(): # Mod can be extracted as is self.__is_flat = False self.__check_if_already_installed() return case _: config_toml = path / "config.toml" if config_toml.exists() and config_toml.is_file(): # Mod needs to be extracted in a folder self.__is_flat = True self.__check_if_already_installed() return # If we ever get here there's either no mod on the archive, or we failed to find it raise NoModExceptionError(str(mod_path)) else: raise UnsupportedArchiveTypeError(str(mod_path)) @property def is_installed(self) -> bool: return self.__is_installed def install(self) -> None: """ Install the mod to Project Diva's mod folder """ if self.__is_installed: return game_path: Path = GameFinder.find() if self.__is_flat: mod_folder_name: str = self.__get_mod_folder_name() ZipFile(self.__archive_path).extractall( path=str(game_path / "mods" / mod_folder_name) ) else: ZipFile(self.__archive_path).extractall(path=str(game_path / "mods")) self.__is_installed = True def __get_mod_folder_name(self) -> str: mod_folder_name: str if self.__is_flat: mod_folder_name = self.__archive_path.stem else: path: ZipPath = ZipPath(ZipFile(self.__archive_path)) things_in_root: list[ZipPath] = list(path.iterdir()) mod_folder_name = things_in_root[0].stem return mod_folder_name def __check_if_already_installed(self) -> None: installed_mod_path: Path = ( GameFinder.find() / "mods" / self.__get_mod_folder_name() ) if installed_mod_path.exists(): self.__is_installed = True class UnsupportedArchiveTypeError(Exception): """ Rased when ModInstaller runs into an archive that it can't extract """ __message: str def __init__(self, archive_path: str) -> None: self.__message = f"Don't know how to unpack archive at {archive_path}" def __str__(self) -> str: return f"{self.__message}" class EmptyArchiveError(Exception): pass class NoModExceptionError(Exception): """ Raised if the archive does not have a mod inside """ __message: str def __init__(self, archive_path: str) -> None: self.__message = f"Archive at {archive_path} does not have a mod" def __str__(self) -> str: return f"{self.__message}"