Initial modpack installation implementation

This commit is contained in:
Евгений Титаренко 2023-01-10 22:55:42 +03:00
parent 1e923dacb9
commit 983d0f9e21
4 changed files with 117 additions and 52 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
__pycache__/
*.swp
.idea

20
api.py
View file

@ -1,22 +1,22 @@
#from objects.api-objects import *
import requests
from tqdm import tqdm
import json
API_VERSION = "v2"
HEADERS = {
'User-Agent': 'mc-get-testing'
}
def download(file_url: str, file_size: int, path: str):
resp = requests.get(file_url, stream=True, headers=HEADERS)
with open(path, 'wb') as file:
for data in tqdm(resp.iter_content(), total=file_size,\
unit_scale=True, unit="byte"):
for data in tqdm(resp.iter_content(), total=file_size, unit_scale=True, unit="byte"):
file.write(data)
def __method(method: str, api_version: str = API_VERSION):
api_url = "https://api.modrinth.com"
def request(**args):
sub_method = ""
if "project" in args:
@ -24,21 +24,19 @@ def __method(method:str, api_version:str=API_VERSION):
elif "version" in args:
sub_method = "/" + args.pop("version")
print(f"{api_url}/{api_version}{method}{sub_method}")
resp = requests.get(f"{api_url}/{api_version}{method}{sub_method}",\
params=args, headers=HEADERS)
resp = requests.get(f"{api_url}/{api_version}{method}{sub_method}", params=args, headers=HEADERS)
print(resp.headers.get("X-Ratelimit-Remaining", -1))
match resp.status_code:
case 200:
print("200: OK")
if type(resp.json()) == list:
return [type(method, (object,), obj)\
for obj in resp.json()]
return [type(method, (object,), obj) for obj in resp.json()]
return type(method, (object,), resp.json())
case 400:
#invalid request
# Invalid request
print("400: ERROR")
case 401:
# No autorization
# No authorization
print("401: ERROR")
case 404:
'''
@ -49,9 +47,9 @@ def __method(method:str, api_version:str=API_VERSION):
resp.raise_for_status()
return request
test = __method("", "")
search = __method("/search")
project = __method("/project")
version = __method("/version")
versions = __method("/versions")

View file

@ -6,41 +6,71 @@ import npyscreen
import os
from colorama import Fore, Style
def validate():
pass
def version_selector_GUI(vers:list, project:str):
def validate():
raise NotImplementedError
def version_selector_gui(vers: list, project: str):
def form(*args):
F = npyscreen.Form(name=f"Select {project} version")
sel = F.add(npyscreen.TitleSelectOne, value=[0,], name="versions:",\
values=[ver.version_number + " for " +\
", ".join(ver.game_versions)\
f = npyscreen.Form(name=f"Select {project} version")
sel = f.add(npyscreen.TitleSelectOne, value=[0, ], name="versions:",
values=[ver.version_number + " for " +
", ".join(ver.game_versions)
for ver in vers[::-1]], scroll_exit=True)
F.edit()
f.edit()
for ver in vers:
if ver.version_number == sel.get_selected_objects()[0].split()[0]:
return ver
return vers[0]
return form
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):
to_install = []
for project in projects:
project_data = api.project(project=project)
to_install.append(project_data)
for project in to_install:
versions = api.versions(ids=str(project.versions).replace("'", '"')) #i hate this
version = npyscreen.wrapper_basic(version_selector_GUI(versions,\
versions = api.versions(ids=str(project.versions).replace("'", '"')) # I hate this
version = npyscreen.wrapper_basic(version_selector_gui(versions,
project.slug))
file = type("mc_file", (object,), version.files[0])
filename = file.url.split("/")[-1]
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(file.url, file.size, cache_file_path)
else:
print(f"{filename} is in cache.")
download_cache(file.url, filename, file.size)
# 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(file.url, file.size, cache_file_path)
# else:
# print(f"{filename} is in cache.")
subdir = ""
match project.project_type:
@ -50,22 +80,26 @@ def install(projects:list):
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")}')
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):
@ -78,11 +112,13 @@ def clean():
print("Nothing to clear.")
if __name__ == "__main__":
desc = "Minecraft mods packet manager based on Modrinth API"
parser = argparse.ArgumentParser(description=desc,\
parser = argparse.ArgumentParser(description=desc,
formatter_class=argparse.RawTextHelpFormatter)
subparsers = parser.add_subparsers(dest="method", required=True) # Переменная, в которую будет записано имя подкоманды
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="+")
@ -95,5 +131,6 @@ if __name__ == "__main__":
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, заодно вытаскивая название метода из списка аргументов,
globals()[kwargs.pop("method")](**kwargs) # Из глобального контекста получаем функцию с названием как в method,
# заодно вытаскивая название метода из списка аргументов,
# затем вызываем функцию с распакованным словарём в качестве аргумента

37
mcfs.py
View file

@ -1,6 +1,9 @@
import os
from sys import platform
import shutil
import zipfile
import json
def __get_mc_dir():
directory = ""
@ -15,6 +18,7 @@ def __get_mc_dir():
directory = os.getenv("MC_DIR", directory)
return directory
def __get_cache_dir():
directory = ""
if platform == 'win32':
@ -30,15 +34,40 @@ def __get_cache_dir():
directory = cache + "/mc-get"
return directory
def get_modpack_info(filename: str):
with zipfile.ZipFile(os.path.join(cache_dir, filename)) as modpack:
with modpack.open("modrinth.index.json") as index:
return type("modpack_index", (object,), json.load(index))
def install_modpacks_override(filename: str):
with zipfile.ZipFile(os.path.join(cache_dir, filename)) as modpack:
files = filter(lambda x: x.filename.startswith("overrides/"), modpack.infolist())
for file in files:
if file.is_dir():
continue
_to_path = os.path.join(mc_dir, file.filename[len("overrides/"):])
os.makedirs(os.path.dirname(_to_path), exist_ok=True)
print(file.filename[len("overrides/"):])
with modpack.open(file) as _from, open(_to_path, 'wb') as _to:
shutil.copyfileobj(_from, _to)
def is_path_exist(path: str):
return os.path.exists(os.path.join(path))
def is_standart_dir_structure():
return not os.path.exists(os.path.join(directory, "home"))
def is_standard_dir_structure():
return not os.path.exists(os.path.join(mc_dir, "home"))
def install(filename, subdir: str):
shutil.copy2(os.path.join(cache_dir,filename),\
os.path.join(mc_dir,subdir,filename))
_from = os.path.join(cache_dir, filename)
_to = os.path.join(mc_dir, subdir, filename)
os.makedirs(os.path.dirname(_to), exist_ok=True)
shutil.copy2(_from, _to)
mc_dir = __get_mc_dir()
cache_dir = __get_cache_dir()