Delete instances/forge-1.20.1-survival/download_mods.py
This commit is contained in:
parent
18b930f5cc
commit
e2c1d50a44
1 changed files with 0 additions and 231 deletions
|
|
@ -1,231 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
# Configuration
|
||||
TARGET_DIR = "./data/mods"
|
||||
MC_VERSION = "1.20.1"
|
||||
LOADER = "forge"
|
||||
MAX_WORKERS = 8
|
||||
USER_AGENT = "MCServerModDownloader/2.0 (admin@server.local)"
|
||||
|
||||
# Explicit Slug Overrides for Modrinth
|
||||
|
||||
SLUG_OVERRIDES = {
|
||||
"perspatium": "ad-astra-per-spatium-et-tempus",
|
||||
"planets+": "ad-astra-planets",
|
||||
"ad_astra_more_structures": "ad-astra-more-structures",
|
||||
"alexsmobs": "alexs-mobs",
|
||||
"architectury": "architectury-api",
|
||||
"cloth-config": "cloth-config",
|
||||
"create_ad_astra_recipes": "create-ad-astra-recipes",
|
||||
"ferritecore": "ferrite-core",
|
||||
"flan": "flan",
|
||||
"improvedmobs": "improved-mobs",
|
||||
"jei": "jei",
|
||||
"patchouli": "patchouli",
|
||||
"player-animation-lib": "playeranimator",
|
||||
"resourcefulconfig": "resourceful-config",
|
||||
"resourcefullib": "resourceful-lib",
|
||||
"simplebackups": "simple-backups",
|
||||
"sophisticatedcore": "sophisticated-core",
|
||||
"tectonic": "tectonic",
|
||||
"spark": "spark",
|
||||
"tenshilib": "tenshilib",
|
||||
"voicechat": "simple-voice-chat",
|
||||
"yungsapi": "yungs-api",
|
||||
"yungsbetterdungeons": "yungs-better-dungeons",
|
||||
"yungsbettermineshafts": "yungs-better-mineshafts",
|
||||
"yungsbetterstrongholds": "yungs-better-strongholds",
|
||||
}
|
||||
|
||||
SERVER_MODS = [
|
||||
"[forge]ctov-3.4.14.jar",
|
||||
"ad_astra-forge-1.20.1-1.15.20.jar",
|
||||
"ad_astra_extra_additions-1.20.1-1.1.1.jar",
|
||||
"ad_astra_more_structures-1.0.1-forge.jar",
|
||||
"alexsmobs-1.22.9.jar",
|
||||
"architectury-9.2.14-forge.jar",
|
||||
"bettercombat-forge-1.9.0+1.20.1.jar",
|
||||
"Botania-1.20.1-454-FORGE.jar",
|
||||
"botarium-forge-1.20.1-2.3.4.jar",
|
||||
"citadel-2.6.3-1.20.1.jar",
|
||||
"cloth-config-11.1.136-forge.jar",
|
||||
"Clumps-forge-1.20.1-12.0.0.4.jar",
|
||||
"connectivity-1.20.1-7.6.jar",
|
||||
"corpse-forge-1.20.1-1.0.23.jar",
|
||||
"create-1.20.1-6.0.8.jar",
|
||||
"create_ad_astra_recipes-1.0.0-forge-1.20.1.jar",
|
||||
"createaddition-1.20.1-1.3.3.jar",
|
||||
"cupboard-1.20.1-3.7.jar",
|
||||
"curios-forge-5.14.1+1.20.1.jar",
|
||||
"decocraft-3.0.4-1.20.1-slim.jar",
|
||||
"DistantHorizons-2.1.2-a-1.20.1.jar",
|
||||
"FarmersDelight-1.20.1-1.3.2.jar",
|
||||
"ferritecore-6.0.1-forge.jar",
|
||||
"flan-1.20.1-1.11.16-forge.jar",
|
||||
"geckolib-forge-1.20.1-4.8.4.jar",
|
||||
"ImmersiveEngineering-1.20.1-10.2.0-183.jar",
|
||||
"improvedmobs-1.20.1-1.13.7-forge.jar",
|
||||
"irons_lib-1.20.1-2.1.0.jar",
|
||||
"irons_spellbooks-1.20.1-3.16.2.jar",
|
||||
"Jade-1.20.1-Forge-11.13.3.jar",
|
||||
"jei-1.20.1-forge-15.48.0.180.jar",
|
||||
"journeymap-forge-1.20.1-6.0.0.jar",
|
||||
"Krypton Reno-forge-1.20.1-26.1.1-1.20.1.jar",
|
||||
"lithostitched-forge-1.20.1-1.4.11.jar",
|
||||
"locks_reforged-1.6.1.jar",
|
||||
"lts_auth-1.0.1+mc1.20.1.jar",
|
||||
"LuckPerms-Forge-5.4.102.jar",
|
||||
"majrusz-library-forge-1.20.1-7.0.8.jar",
|
||||
"majruszs-difficulty-forge-1.20.1-1.9.10.jar",
|
||||
"mcda-5.0.2.jar",
|
||||
"mcdw-9.0.4.jar",
|
||||
"minecraft-comes-alive-7.6.28-beta.10+1.20.1-universal.jar",
|
||||
"modernfix-forge-5.27.66+mc1.20.1.jar",
|
||||
"Nullscape_1.20.x_v1.2.8.jar",
|
||||
"Patchouli-1.20.1-85-FORGE.jar",
|
||||
"perspatium-1.20.1-1.2.0.jar",
|
||||
"planets+-bv1.7.5-1.20x.jar",
|
||||
"player-animation-lib-forge-1.0.2-rc1+1.20.jar",
|
||||
"resourcefulconfig-forge-1.20.1-2.1.3.jar",
|
||||
"resourcefullib-forge-1.20.1-2.1.29.jar",
|
||||
"SimpleBackups-1.20.1-3.1.24.jar",
|
||||
"sophisticatedbackpacks-1.20.1-3.24.63.2049.jar",
|
||||
"sophisticatedcore-1.20.1-1.3.75.2231.jar",
|
||||
"spark-1.10.53-forge.jar",
|
||||
"tectonic-3.0.17-forge-1.20.1.jar",
|
||||
"tenshilib-1.20.1-1.7.6-forge.jar",
|
||||
"Terralith_1.20.x_v2.5.4.jar",
|
||||
"voicechat-forge-1.20.1-2.6.22.jar",
|
||||
"YungsApi-1.20-Forge-4.0.6.jar",
|
||||
"YungsBetterDungeons-1.20-Forge-4.0.4.jar",
|
||||
"YungsBetterMineshafts-1.20-Forge-4.0.4.jar",
|
||||
"YungsBetterStrongholds-1.20-Forge-4.0.3.jar",
|
||||
]
|
||||
|
||||
def clean_mod_name(filename):
|
||||
"""Strip prefixed metadata and digits to extract purely the mod identifier."""
|
||||
name = re.sub(r"^\[.*?\]", "", filename)
|
||||
name = re.sub(r"\.jar$", "", name)
|
||||
|
||||
# Split tokens by hyphen/underscore and keep tokens before version digits
|
||||
tokens = re.split(r"[-_]", name)
|
||||
clean_tokens = []
|
||||
for token in tokens:
|
||||
if re.match(r"^(v?\d|bv?\d|forge|fabric|quilt|mc1|1\.20|universal|slim)", token, re.I):
|
||||
break
|
||||
clean_tokens.append(token)
|
||||
|
||||
result = "-".join(clean_tokens).strip("-")
|
||||
return result if result else name
|
||||
|
||||
def http_get_json(url):
|
||||
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
def download_file(url, target_path):
|
||||
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
|
||||
with urllib.request.urlopen(req, timeout=30) as resp, open(target_path, "wb") as f:
|
||||
f.write(resp.read())
|
||||
|
||||
def process_mod(jar_name):
|
||||
target_path = os.path.join(TARGET_DIR, jar_name)
|
||||
|
||||
if os.path.exists(target_path):
|
||||
return ("EXISTS", jar_name, "File already in destination")
|
||||
|
||||
raw_clean = clean_mod_name(jar_name)
|
||||
slug = SLUG_OVERRIDES.get(raw_clean.lower(), raw_clean.lower())
|
||||
|
||||
project_id = None
|
||||
|
||||
# Step 1: Try direct project lookup by slug
|
||||
try:
|
||||
proj_data = http_get_json(f"https://api.modrinth.com/v2/project/{slug}")
|
||||
if proj_data and "id" in proj_data:
|
||||
project_id = proj_data["id"]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Step 2: Fallback search if direct slug lookup fails
|
||||
if not project_id:
|
||||
try:
|
||||
search_q = slug.replace("-", " ")
|
||||
facets = json.dumps([["categories:" + LOADER], ["versions:" + MC_VERSION]])
|
||||
query_url = f"https://api.modrinth.com/v2/search?query={urllib.parse.quote(search_q)}&facets={urllib.parse.quote(facets)}"
|
||||
data = http_get_json(query_url)
|
||||
hits = data.get("hits", [])
|
||||
if hits:
|
||||
project_id = hits[0]["project_id"]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not project_id:
|
||||
return ("NOT_FOUND", jar_name, f"Could not resolve Modrinth project for '{slug}'")
|
||||
|
||||
# Step 3: Fetch versions and download
|
||||
try:
|
||||
ver_url = f"https://api.modrinth.com/v2/project/{project_id}/version?loaders=[%22{LOADER}%22]&game_versions=[%22{MC_VERSION}%22]"
|
||||
versions = http_get_json(ver_url)
|
||||
|
||||
if not versions:
|
||||
return ("NOT_FOUND", jar_name, f"No {LOADER} {MC_VERSION} release on Modrinth")
|
||||
|
||||
download_url = None
|
||||
for ver in versions:
|
||||
for f in ver.get("files", []):
|
||||
if f.get("filename") == jar_name:
|
||||
download_url = f.get("url")
|
||||
break
|
||||
if download_url:
|
||||
break
|
||||
|
||||
if not download_url and versions[0].get("files"):
|
||||
download_url = versions[0]["files"][0].get("url")
|
||||
|
||||
if download_url:
|
||||
download_file(download_url, target_path)
|
||||
return ("SUCCESS", jar_name, "Downloaded from Modrinth")
|
||||
else:
|
||||
return ("NOT_FOUND", jar_name, "No download URL available")
|
||||
|
||||
except Exception as e:
|
||||
return ("ERROR", jar_name, str(e))
|
||||
|
||||
def main():
|
||||
os.makedirs(TARGET_DIR, exist_ok=True)
|
||||
print(f"🚀 Starting download for {len(SERVER_MODS)} mods into '{TARGET_DIR}'...")
|
||||
print(f"⚙️ Target: Minecraft {MC_VERSION} ({LOADER.upper()}) | Workers: {MAX_WORKERS}\n")
|
||||
|
||||
results = {"SUCCESS": [], "EXISTS": [], "NOT_FOUND": [], "ERROR": []}
|
||||
|
||||
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
|
||||
futures = {executor.submit(process_mod, mod): mod for mod in SERVER_MODS}
|
||||
for future in as_completed(futures):
|
||||
status, jar_name, msg = future.result()
|
||||
results[status].append((jar_name, msg))
|
||||
|
||||
symbol = "✅" if status == "SUCCESS" else "⏭️" if status == "EXISTS" else "❌"
|
||||
print(f"{symbol} [{status}] {jar_name} -> {msg}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("📊 DOWNLOAD SUMMARY")
|
||||
print("=" * 60)
|
||||
print(f" ✅ Downloaded: {len(results['SUCCESS'])}")
|
||||
print(f" ⏭️ Skipped: {len(results['EXISTS'])}")
|
||||
print(f" ❌ Not Found: {len(results['NOT_FOUND']) + len(results['ERROR'])}")
|
||||
|
||||
missing = results['NOT_FOUND'] + results['ERROR']
|
||||
if missing:
|
||||
print("\n⚠️ The following mods were not found automatically:")
|
||||
for jar_name, msg in missing:
|
||||
print(f" • {jar_name} ({msg})")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in a new issue