#!/usr/bin/env python3 import argparse import api import mcfs import os import mcgetdb from colorama import Fore, Style db = mcgetdb.McGetDB(mcfs.mc_dir) def validate(): raise NotImplementedError def download_cache(url: str, filename: str, size: int): cache_file_path = os.path.join(mcfs.cache_dir, filename) if not mcfs.is_path_exist(mcfs.cache_dir): os.mkdir(mcfs.cache_dir) if not mcfs.is_path_exist(cache_file_path): api.download(url, size, cache_file_path) else: print(f"{filename} is in cache.") return filename def modpack_install(filename: str): modpack = mcfs.get_modpack_info(filename) print(f"{Fore.YELLOW}Installing {modpack.name} modpack...{Style.RESET_ALL}") for file in modpack.files: path = file["path"].split("/") downloads = file["downloads"] print(file["path"]) download_cache(downloads[0], path[1], file["fileSize"]) mcfs.install(path[1], path[0]) print(f"{Fore.YELLOW}Overriding...{Style.RESET_ALL}") mcfs.install_modpacks_override(filename) def install(projects: list, version, loader): to_install = [] dependencies_to_install = [] not_found = [] unavailable = [] projects_ids = [] def check_project(slug): id = api.check_project(project=slug) if id: projects_ids.append(id.id) else: not_found.append(slug) def dependency_solver(ver): for dependency in ver.dependencies: if dependency["dependency_type"] in ["optional", "embedded"]: continue if dependency["version_id"]: dep_ver = api.version(version=dependency["version_id"]) else: dep_ver = api.get_versions(project=dependency["project_id"], loaders=f'["{loader}"]', game_versions=f'["{mc_ver}"]')[0] if dependency["project_id"]: proj_id = dependency["project_id"] else: proj_id = dep_ver.project_id if proj_id in projects_ids or proj_id in [dep_proj.id for dep_proj, _ in dependencies_to_install]: continue project_data = api.project(project=proj_id) dependencies_to_install.append((project_data, dep_ver)) dependency_solver(dep_ver) for project in projects: check_project(project) for project in projects_ids: project_data = api.project(project=project) versions = api.get_versions(project=project, loaders=f'["{loader}"]', game_versions=f'["{mc_ver}"]') if versions: to_install.append((project_data, versions[0])) dependency_solver(versions[0]) else: unavailable.append(project_data) print("To install:", *[project.title + " " + version.version_number for project, version in to_install], sep="\n\t") if dependencies_to_install: print("With dependencies:", *[project.title + " " + version.version_number for project, version in dependencies_to_install], sep="\n\t") if not_found: print("Not found: ", end="") print(*[project for project in not_found], sep=", ") if unavailable: print("Cannot be installed:", *[project.title for project in unavailable], sep="\n\t") choose = input("Continue? [y/n]") if choose.strip().lower() in ["n", "no"]: print("Canceled.") return for project, version in to_install + dependencies_to_install: file = type("mc_file", (object,), version.files[0]) filename = project.slug + ".jar" download_cache(file.url, filename, file.size) match project.project_type: case "resourcepack": subdir = "resourcepacks" case "mod": subdir = "mods" case "shader": subdir = "shaderpacks" case "modpack": modpack_install(filename) continue case _: raise NotImplementedError mcfs.install(filename, subdir) def search(query: list): results = api.search(query=' '.join(query)) for result in results.hits: print(Fore.GREEN, end="") print(result.get("slug", "error"), end="") print(Style.RESET_ALL, end="") print(f' [{result.get("project_type", "error")}] ', end="") print( f' : {Fore.GREEN}{result.get("title", "error")}{Style.RESET_ALL} --- {result.get("description", "error")}') def clean(): if mcfs.is_path_exist(mcfs.cache_dir): files = os.listdir(mcfs.cache_dir) if len(files) > 0: for file in files: os.remove(os.path.join(mcfs.cache_dir, file)) print("Cache cleared successfully.") return print("Nothing to clear.") if __name__ == "__main__": def __select_version(versions): if len(versions) > 0: print("Installed MC versions: ") for id, version in enumerate(versions): print(id + 1, version.version_number + (f" with {version.modloader}" if version.is_modified else ""), sep=": ") if len(versions) > 1: id_to_use = -1 while id_to_use not in range(1, len(versions)): id_input = input(f"Select MC version to use [1-{len(versions)}]: ") if not id_input.isnumeric(): continue id_to_use = int(id_input) else: id_to_use = 1 selected_version = versions[id_to_use - 1] print("Selected MC version:", selected_version.version_number + (f" with {selected_version.modloader}" if selected_version.is_modified else "")) return selected_version properties = db.get_properties() if not properties: version_to_use = __select_version(mcfs.get_installed_mc_versions()) if version_to_use.is_modified: # TODO: Добавить иерархию каталогов db.set_properties(version_to_use.version_number, version_to_use.modloader) else: db.set_properties(version_to_use.version_number) properties = db.get_properties() print(properties) _, mc_ver, loader, _ = properties desc = "Minecraft mods packet manager based on Modrinth API" parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawTextHelpFormatter) subparsers = parser.add_subparsers(dest="method", required=True) # Переменная, в которую будет записано имя подкоманды parser_install = subparsers.add_parser("install", help="Install one or more mods or resources") parser_install.add_argument("projects", nargs="+") parser_install.add_argument("--version", default=mc_ver) parser_install.add_argument("--loader", default=loader) parser_search = subparsers.add_parser("search", help="Find a mod or a resource") parser_search.add_argument("query", nargs="+") parser_validate = subparsers.add_parser("validate", help="Validate the installation") parser_clean = subparsers.add_parser("clean", help="Clean the cache of this program") kwargs = vars(parser.parse_args()) # Получаем все поля получившегося Namespace и пихаем в словарь globals()[kwargs.pop("method")](**kwargs) # Из глобального контекста получаем функцию с названием как в method, # заодно вытаскивая название метода из списка аргументов, # затем вызываем функцию с распакованным словарём в качестве аргумента