Created
January 20, 2024 01:53
-
-
Save RedstoneWizard08/9a731c53178228fd49c141e2730b8e70 to your computer and use it in GitHub Desktop.
py2modman cli?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
#################################################################### | |
py2modman-cli.py v0.1.0 | |
A simple program to download & install a profile from Thunderstore. | |
Designed for Lethal Company (and my modpack) by @RedstoneWizard08 | |
Licensed under the MIT license. | |
==== Usage ==== | |
./py2modman-cli.py [code] | |
^^^ Installs the profile from the code [code] to the "pack" folder | |
in the current directory. | |
==== Examples ==== | |
bash $ ./py2modman-cli.py 018d1dcb-6035-3764-328f-fd149b33d6a2 | |
[INFO] Fetching profile: 018d1dcb-6035-3764-328f-fd149b33d6a2 | |
[INFO] Found profile: "Fresh" | |
[1 of 41] Downloading: BepInEx/BepInExPack v5.4.2100 | |
# ... | |
[41 of 41] Downloading: Tyzeron/Minimap v1.0.5 | |
bash $ | |
==== Speed ==== | |
Speed may vary based on your CPU, internet connection, disk speed, | |
and many other factors. This is my test on a cloud server with 5 | |
cores and 26 GB of RAM (arm64), while running a Minecraft server in | |
the background (I did this quickly xD). | |
bash $ time ./py2modman-cli.py 018d1dcb-6035-3764-328f-fd149b33d6a2 | |
# ... | |
[...] 4.03s user 1.08s system 29% cpu 17.410 total | |
bash $ | |
#################################################################### | |
""" | |
import io | |
import os | |
import glob | |
import yaml | |
import base64 | |
import shutil | |
import zipfile | |
import requests | |
import argparse | |
def fix_paths(dir: str): | |
for item in glob.glob(dir): | |
if os.path.isdir(item): | |
continue | |
if "\\" in item: | |
real = item.replace("\\", "/") | |
parent = os.path.dirname(real) | |
if not os.path.exists(parent): | |
os.makedirs(parent) | |
os.rename(item, real) | |
def rm_or_not(file: str): | |
if os.path.exists(file): | |
os.remove(file) | |
def main(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument("code", type=str) | |
args = parser.parse_args() | |
code = args.code | |
print(f"[INFO] Fetching profile: {code}") | |
url = f"https://thunderstore.io/api/experimental/legacyprofile/get/{code}/" | |
data = requests.get(url) | |
data = data.text.replace("#r2modman", "") | |
data = base64.b64decode(data) | |
buf = io.BytesIO(data) | |
if os.path.exists("pack"): | |
shutil.rmtree("pack") | |
os.makedirs("pack") | |
zipf = zipfile.ZipFile(buf) | |
zipf.extractall(path="pack") | |
zipf.close() | |
fix_paths("pack/**") | |
shutil.move("pack/config", "pack/BepInEx/config") | |
with open("pack/export.r2x") as file: | |
export_raw = file.read() | |
export = yaml.safe_load(export_raw) | |
mods = export["mods"] | |
name = export["profileName"] | |
print(f"[INFO] Found profile: \"{name}\"") | |
for (i, mod) in enumerate(mods): | |
ns = mod["name"].split("-")[0] | |
id = "-".join(mod["name"].split("-")[1:]) | |
ver = ".".join(str(v) for v in mod["version"].values()) | |
print(f"[{i + 1} of {len(mods)}] Downloading: {ns}/{id} v{ver}") | |
url = f"https://thunderstore.io/package/download/{ns}/{id}/{ver}/" | |
dir = f"pack/BepInEx/plugins/{ns}-{id}" | |
data = requests.get(url).content | |
buf = io.BytesIO(data) | |
zipf = zipfile.ZipFile(buf) | |
infos = [v.filename for v in zipf.filelist] | |
if id == "BepInExPack": | |
zipf.extractall("pack") | |
shutil.move("pack/BepInExPack/BepInEx/core", "pack/BepInEx/core") | |
os.rename("pack/BepInExPack/BepInEx/config/BepInEx.cfg", "pack/BepInEx/config/BepInEx.cfg") | |
os.rename("pack/BepInExPack/winhttp.dll", "pack/winhttp.dll") | |
os.rename("pack/BepInExPack/doorstop_config.ini", "pack/doorstop_config.ini") | |
shutil.rmtree("pack/BepInExPack") | |
rm_or_not("pack/icon.png") | |
rm_or_not("pack/LICENSE") | |
rm_or_not("pack/README.md") | |
rm_or_not("pack/CHANGELOG.md") | |
rm_or_not("pack/manifest.json") | |
else: | |
if "BepInEx/" in infos: | |
zipf.extractall("pack") | |
rm_or_not("pack/icon.png") | |
rm_or_not("pack/LICENSE") | |
rm_or_not("pack/README.md") | |
rm_or_not("pack/CHANGELOG.md") | |
rm_or_not("pack/manifest.json") | |
elif "plugins/" in infos or "patchers/" in infos: | |
zipf.extractall("pack/BepInEx") | |
rm_or_not("pack/BepInEx/icon.png") | |
rm_or_not("pack/BepInEx/LICENSE") | |
rm_or_not("pack/BepInEx/README.md") | |
rm_or_not("pack/BepInEx/CHANGELOG.md") | |
rm_or_not("pack/BepInEx/manifest.json") | |
elif ns + "-" + id + "/BepInEx" in infos: | |
zipf.extractall("pack") | |
rm_or_not("pack/icon.png") | |
rm_or_not("pack/LICENSE") | |
rm_or_not("pack/README.md") | |
rm_or_not("pack/CHANGELOG.md") | |
rm_or_not("pack/manifest.json") | |
for f in os.listdir("pack/" + ns + "-" + id): | |
src = "pack/" + ns + "-" + id + f | |
dst = "pack/" + f | |
shutil.move(src, dst) | |
elif id + "/BepInEx" in infos: | |
zipf.extractall("pack") | |
rm_or_not("pack/icon.png") | |
rm_or_not("pack/LICENSE") | |
rm_or_not("pack/README.md") | |
rm_or_not("pack/CHANGELOG.md") | |
rm_or_not("pack/manifest.json") | |
for f in os.listdir("pack/" + id): | |
src = "pack/" + id + f | |
dst = "pack/" + f | |
shutil.move(src, dst) | |
elif ns + "-" + id + "/plugins" in infos or ns + "-" + id + "/patchers" in infos: | |
zipf.extractall("pack") | |
rm_or_not("pack/icon.png") | |
rm_or_not("pack/LICENSE") | |
rm_or_not("pack/README.md") | |
rm_or_not("pack/CHANGELOG.md") | |
rm_or_not("pack/manifest.json") | |
for f in os.listdir("pack/" + ns + "-" + id): | |
src = "pack/" + ns + "-" + id + f | |
dst = "pack/BepInEx/" + f | |
shutil.move(src, dst) | |
elif id + "/plugins" in infos or id + "/patchers" in infos: | |
zipf.extractall("pack") | |
rm_or_not("pack/icon.png") | |
rm_or_not("pack/LICENSE") | |
rm_or_not("pack/README.md") | |
rm_or_not("pack/CHANGELOG.md") | |
rm_or_not("pack/manifest.json") | |
for f in os.listdir("pack/" + id): | |
src = "pack/" + id + f | |
dst = "pack/BepInEx/" + f | |
shutil.move(src, dst) | |
else: | |
zipf.extractall(dir) | |
rm_or_not(dir + "/icon.png") | |
rm_or_not(dir + "/LICENSE") | |
rm_or_not(dir + "/README.md") | |
rm_or_not(dir + "/CHANGELOG.md") | |
rm_or_not(dir + "/manifest.json") | |
zipf.close() | |
fix_paths("pack/**") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment