Доработка взаимодействия с API, новая процедура установки, определение установленных версий игры и начальная реализация БД
This commit is contained in:
parent
983d0f9e21
commit
5beb835d1e
4 changed files with 217 additions and 51 deletions
26
api.py
26
api.py
|
@ -14,21 +14,22 @@ def download(file_url: str, file_size: int, path: str):
|
|||
file.write(data)
|
||||
|
||||
|
||||
def __method(method: str, api_version: str = API_VERSION):
|
||||
def __method(method: str, sub_method = "", api_version: str = API_VERSION):
|
||||
api_url = "https://api.modrinth.com"
|
||||
|
||||
def request(**args):
|
||||
sub_method = ""
|
||||
slug = ""
|
||||
if "project" in args:
|
||||
sub_method = "/" + args.pop("project")
|
||||
slug = "/" + args.pop("project")
|
||||
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)
|
||||
print(resp.headers.get("X-Ratelimit-Remaining", -1))
|
||||
slug = "/" + args.pop("version")
|
||||
# print(f"{api_url}/{api_version}{method}{slug}{sub_method}")
|
||||
#print(args)
|
||||
resp = requests.get(f"{api_url}/{api_version}{method}{slug}{sub_method}", params=args, headers=HEADERS)
|
||||
# print(resp.headers.get("X-Ratelimit-Remaining", -1))
|
||||
match resp.status_code:
|
||||
case 200:
|
||||
print("200: OK")
|
||||
# print("200: OK")
|
||||
if type(resp.json()) == list:
|
||||
return [type(method, (object,), obj) for obj in resp.json()]
|
||||
return type(method, (object,), resp.json())
|
||||
|
@ -40,10 +41,10 @@ def __method(method: str, api_version: str = API_VERSION):
|
|||
print("401: ERROR")
|
||||
case 404:
|
||||
'''
|
||||
The requested project was not found or
|
||||
no authorization to see this project
|
||||
The requested project was not found or no authorization to see this project
|
||||
'''
|
||||
print("404: ERROR")
|
||||
# print("404: ERROR")
|
||||
return None
|
||||
resp.raise_for_status()
|
||||
return request
|
||||
|
||||
|
@ -51,5 +52,6 @@ def __method(method: str, api_version: str = API_VERSION):
|
|||
test = __method("", "")
|
||||
search = __method("/search")
|
||||
project = __method("/project")
|
||||
check_project = __method("/project", sub_method="/check")
|
||||
version = __method("/version")
|
||||
versions = __method("/versions")
|
||||
get_versions = __method("/project", sub_method="/version")
|
||||
|
|
130
mc-get.py
130
mc-get.py
|
@ -2,31 +2,16 @@
|
|||
import argparse
|
||||
import api
|
||||
import mcfs
|
||||
import npyscreen
|
||||
import os
|
||||
import mcgetdb
|
||||
from colorama import Fore, Style
|
||||
|
||||
db = mcgetdb.McGetDB(mcfs.mc_dir)
|
||||
|
||||
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)
|
||||
for ver in vers[::-1]], scroll_exit=True)
|
||||
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):
|
||||
|
@ -51,28 +36,72 @@ def modpack_install(filename: str):
|
|||
mcfs.install_modpacks_override(filename)
|
||||
|
||||
|
||||
def install(projects: list):
|
||||
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)
|
||||
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,
|
||||
project.slug))
|
||||
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 = file.url.split("/")[-1]
|
||||
filename = project.slug + ".jar"
|
||||
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:
|
||||
case "resourcepack":
|
||||
subdir = "resourcepacks"
|
||||
|
@ -114,6 +143,39 @@ def clean():
|
|||
|
||||
|
||||
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)
|
||||
|
@ -122,6 +184,8 @@ if __name__ == "__main__":
|
|||
|
||||
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="+")
|
||||
|
|
59
mcfs.py
59
mcfs.py
|
@ -5,6 +5,23 @@ import zipfile
|
|||
import json
|
||||
|
||||
|
||||
class MCVersion:
|
||||
def __init__(self, version_number, is_modified=False, modloader=None):
|
||||
self.version_number = version_number
|
||||
self.is_modified = is_modified
|
||||
self.modloader = modloader
|
||||
|
||||
def __lt__(self, other):
|
||||
own_nums = map(int, self.version_number.split("."))
|
||||
other_nums = map(int, other.version_number.split("."))
|
||||
for own_num, other_num in zip(own_nums, other_nums):
|
||||
if own_num < other_num:
|
||||
return True
|
||||
if own_num > other_num:
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def __get_mc_dir():
|
||||
directory = ""
|
||||
if platform == 'linux':
|
||||
|
@ -35,6 +52,14 @@ def __get_cache_dir():
|
|||
return directory
|
||||
|
||||
|
||||
def __version_sort(mc_ver: MCVersion) -> str:
|
||||
return mc_ver.version_number
|
||||
|
||||
|
||||
mc_dir = __get_mc_dir()
|
||||
cache_dir = __get_cache_dir()
|
||||
|
||||
|
||||
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:
|
||||
|
@ -58,10 +83,6 @@ def is_path_exist(path: str):
|
|||
return os.path.exists(os.path.join(path))
|
||||
|
||||
|
||||
def is_standard_dir_structure():
|
||||
return not os.path.exists(os.path.join(mc_dir, "home"))
|
||||
|
||||
|
||||
def install(filename, subdir: str):
|
||||
_from = os.path.join(cache_dir, filename)
|
||||
_to = os.path.join(mc_dir, subdir, filename)
|
||||
|
@ -69,5 +90,31 @@ def install(filename, subdir: str):
|
|||
shutil.copy2(_from, _to)
|
||||
|
||||
|
||||
mc_dir = __get_mc_dir()
|
||||
cache_dir = __get_cache_dir()
|
||||
def get_installed_mc_versions():
|
||||
mc_vers_dir = os.path.join(mc_dir, "versions")
|
||||
if not is_path_exist(mc_vers_dir):
|
||||
return # TODO: Выброс ошибки
|
||||
versions_dirs = next(os.walk(mc_vers_dir))[1]
|
||||
versions = []
|
||||
for version_dir in versions_dirs:
|
||||
version_json_file = os.path.join(mc_vers_dir, version_dir, version_dir + ".json")
|
||||
if not is_path_exist(version_json_file):
|
||||
continue
|
||||
with open(version_json_file) as json_file:
|
||||
version_json = json.load(json_file)
|
||||
mc_ver = None
|
||||
match version_json.get("type", None):
|
||||
case "modified":
|
||||
version_number = version_json["jar"] # TODO: ИСПОЛЬЗОВАТЬ get ВМЕСТО []
|
||||
is_modified = True
|
||||
modloader = version_json["id"].split()[0].lower()
|
||||
mc_ver = MCVersion(version_number, is_modified, modloader)
|
||||
case "release": # TODO: Добавить поддержку других значений
|
||||
version_number = version_json["id"]
|
||||
mc_ver = MCVersion(version_number)
|
||||
case None:
|
||||
pass
|
||||
case _: # TODO: Throw some errors
|
||||
pass
|
||||
versions.append(mc_ver)
|
||||
return sorted(versions, reverse=True)
|
||||
|
|
53
mcgetdb.py
Normal file
53
mcgetdb.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
import sqlite3
|
||||
import os
|
||||
|
||||
DB_VERSION = 1
|
||||
|
||||
class McGetDB:
|
||||
|
||||
def __init_db(self):
|
||||
with self.db as db:
|
||||
db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS mods (
|
||||
slug TEXT PRIMARY KEY NOT NULL,
|
||||
install_path TEXT NOT NULL,
|
||||
version TEXT NOT NULL
|
||||
);''')
|
||||
db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS properties (
|
||||
db_version TEXT PRIMARY KEY NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
modloader TEXT,
|
||||
dir_hierarchy TEXT
|
||||
);''')
|
||||
|
||||
def __init__(self, mc_path):
|
||||
self.db_path = os.path.join(mc_path, "mcget.db")
|
||||
self.db = sqlite3.connect(self.db_path)
|
||||
self.__init_db()
|
||||
|
||||
def get_properties(self):
|
||||
properties = None
|
||||
with self.db as db:
|
||||
properties = db.execute('''
|
||||
SELECT * FROM properties;
|
||||
''').fetchone()
|
||||
return properties
|
||||
|
||||
def set_properties(self, mc_ver, modloader = "NULL", dir_hierarhy = "default"):
|
||||
with self.db as db:
|
||||
db.execute('''
|
||||
DELETE FROM properties;
|
||||
''')
|
||||
db.execute('''
|
||||
INSERT INTO properties VALUES (?, ?, ?, ?)
|
||||
''', (DB_VERSION, mc_ver, modloader, dir_hierarhy))
|
||||
|
||||
def add_mod(self):
|
||||
pass
|
||||
|
||||
def remove_mod(self):
|
||||
pass
|
||||
|
||||
def update_mod(self):
|
||||
pass
|
Loading…
Add table
Reference in a new issue