Доработка взаимодействия с 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)
|
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"
|
api_url = "https://api.modrinth.com"
|
||||||
|
|
||||||
def request(**args):
|
def request(**args):
|
||||||
sub_method = ""
|
slug = ""
|
||||||
if "project" in args:
|
if "project" in args:
|
||||||
sub_method = "/" + args.pop("project")
|
slug = "/" + args.pop("project")
|
||||||
elif "version" in args:
|
elif "version" in args:
|
||||||
sub_method = "/" + args.pop("version")
|
slug = "/" + args.pop("version")
|
||||||
print(f"{api_url}/{api_version}{method}{sub_method}")
|
# print(f"{api_url}/{api_version}{method}{slug}{sub_method}")
|
||||||
resp = requests.get(f"{api_url}/{api_version}{method}{sub_method}", params=args, headers=HEADERS)
|
#print(args)
|
||||||
print(resp.headers.get("X-Ratelimit-Remaining", -1))
|
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:
|
match resp.status_code:
|
||||||
case 200:
|
case 200:
|
||||||
print("200: OK")
|
# print("200: OK")
|
||||||
if type(resp.json()) == list:
|
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())
|
return type(method, (object,), resp.json())
|
||||||
|
@ -40,10 +41,10 @@ def __method(method: str, api_version: str = API_VERSION):
|
||||||
print("401: ERROR")
|
print("401: ERROR")
|
||||||
case 404:
|
case 404:
|
||||||
'''
|
'''
|
||||||
The requested project was not found or
|
The requested project was not found or no authorization to see this project
|
||||||
no authorization to see this project
|
|
||||||
'''
|
'''
|
||||||
print("404: ERROR")
|
# print("404: ERROR")
|
||||||
|
return None
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
@ -51,5 +52,6 @@ def __method(method: str, api_version: str = API_VERSION):
|
||||||
test = __method("", "")
|
test = __method("", "")
|
||||||
search = __method("/search")
|
search = __method("/search")
|
||||||
project = __method("/project")
|
project = __method("/project")
|
||||||
|
check_project = __method("/project", sub_method="/check")
|
||||||
version = __method("/version")
|
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 argparse
|
||||||
import api
|
import api
|
||||||
import mcfs
|
import mcfs
|
||||||
import npyscreen
|
|
||||||
import os
|
import os
|
||||||
|
import mcgetdb
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
|
|
||||||
|
db = mcgetdb.McGetDB(mcfs.mc_dir)
|
||||||
|
|
||||||
def validate():
|
def validate():
|
||||||
raise NotImplementedError
|
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):
|
def download_cache(url: str, filename: str, size: int):
|
||||||
cache_file_path = os.path.join(mcfs.cache_dir, filename)
|
cache_file_path = os.path.join(mcfs.cache_dir, filename)
|
||||||
if not mcfs.is_path_exist(mcfs.cache_dir):
|
if not mcfs.is_path_exist(mcfs.cache_dir):
|
||||||
|
@ -51,28 +36,72 @@ def modpack_install(filename: str):
|
||||||
mcfs.install_modpacks_override(filename)
|
mcfs.install_modpacks_override(filename)
|
||||||
|
|
||||||
|
|
||||||
def install(projects: list):
|
def install(projects: list, version, loader):
|
||||||
to_install = []
|
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:
|
for project in projects:
|
||||||
|
check_project(project)
|
||||||
|
|
||||||
|
for project in projects_ids:
|
||||||
project_data = api.project(project=project)
|
project_data = api.project(project=project)
|
||||||
to_install.append(project_data)
|
versions = api.get_versions(project=project, loaders=f'["{loader}"]',
|
||||||
for project in to_install:
|
game_versions=f'["{mc_ver}"]')
|
||||||
versions = api.versions(ids=str(project.versions).replace("'", '"')) # I hate this
|
if versions:
|
||||||
version = npyscreen.wrapper_basic(version_selector_gui(versions,
|
to_install.append((project_data, versions[0]))
|
||||||
project.slug))
|
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])
|
file = type("mc_file", (object,), version.files[0])
|
||||||
filename = file.url.split("/")[-1]
|
filename = project.slug + ".jar"
|
||||||
download_cache(file.url, filename, file.size)
|
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:
|
match project.project_type:
|
||||||
case "resourcepack":
|
case "resourcepack":
|
||||||
subdir = "resourcepacks"
|
subdir = "resourcepacks"
|
||||||
|
@ -114,6 +143,39 @@ def clean():
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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"
|
desc = "Minecraft mods packet manager based on Modrinth API"
|
||||||
parser = argparse.ArgumentParser(description=desc,
|
parser = argparse.ArgumentParser(description=desc,
|
||||||
formatter_class=argparse.RawTextHelpFormatter)
|
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 = subparsers.add_parser("install", help="Install one or more mods or resources")
|
||||||
parser_install.add_argument("projects", nargs="+")
|
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 = subparsers.add_parser("search", help="Find a mod or a resource")
|
||||||
parser_search.add_argument("query", nargs="+")
|
parser_search.add_argument("query", nargs="+")
|
||||||
|
|
59
mcfs.py
59
mcfs.py
|
@ -5,6 +5,23 @@ import zipfile
|
||||||
import json
|
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():
|
def __get_mc_dir():
|
||||||
directory = ""
|
directory = ""
|
||||||
if platform == 'linux':
|
if platform == 'linux':
|
||||||
|
@ -35,6 +52,14 @@ def __get_cache_dir():
|
||||||
return directory
|
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):
|
def get_modpack_info(filename: str):
|
||||||
with zipfile.ZipFile(os.path.join(cache_dir, filename)) as modpack:
|
with zipfile.ZipFile(os.path.join(cache_dir, filename)) as modpack:
|
||||||
with modpack.open("modrinth.index.json") as index:
|
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))
|
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):
|
def install(filename, subdir: str):
|
||||||
_from = os.path.join(cache_dir, filename)
|
_from = os.path.join(cache_dir, filename)
|
||||||
_to = os.path.join(mc_dir, subdir, filename)
|
_to = os.path.join(mc_dir, subdir, filename)
|
||||||
|
@ -69,5 +90,31 @@ def install(filename, subdir: str):
|
||||||
shutil.copy2(_from, _to)
|
shutil.copy2(_from, _to)
|
||||||
|
|
||||||
|
|
||||||
mc_dir = __get_mc_dir()
|
def get_installed_mc_versions():
|
||||||
cache_dir = __get_cache_dir()
|
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