diff options
Diffstat (limited to 'update.py')
-rwxr-xr-x | update.py | 248 |
1 files changed, 0 insertions, 248 deletions
diff --git a/update.py b/update.py deleted file mode 100755 index 2851c43..0000000 --- a/update.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import sys -import hashlib -import shutil -import re -import collections -import urllib.parse - -import requests - -parser = argparse.ArgumentParser( - description="A Simple Git-Based Modpack Manager", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog='''\ -Available commands: - install : Downloads mods listed in version.txt and populates the mods folder specified in pack-location.txt - apply_updates : Using the urls in mods.txt, repopulates version.txt to reflect the most recent mod versions - check_updates : Compares version.txt and mods.txt to see if any mods can be updated -''') - -parser.add_argument('command', - nargs='?', - default='install', - help="The action to perform (default: install)") -parser.add_argument('filename', - nargs='?', - default="mods.txt", - help="Optional filename to specify latest mods (default: mods.txt)") -parser.add_argument('--version-file', - type=str, - default="version.txt", - help="Optional custom version file to download mods from (default: version.txt)") -parser.add_argument('--pack-location', - type=str, - help="Optional custom modpack folder location (default: read from pack-location.txt)") -parser.add_argument('--whitelist-file', - type=str, - default="whitelist.txt", - help="Optional custom whitelist file that tells 'install' which files not to remove (default: whitelist.txt)") -parser.add_argument("--game-version", - type=str, - default=None, - help="The maximum game version to update mods to") - -## loaded from version.txt -VERSION = 0 - -def read_file(fil): - strings = [] - with open(fil) as f: - for line in f: - string = line.strip().split() - if len(line) > 1 and line[0] != '#': - # run strip on each element - string = tuple(map(lambda x: x.strip(), string)) - strings.append(string) - - return strings - -# Apply updates to the actual mod pack -def install(args): - print("Updating pack with version " + str(VERSION) + "...") - print() - # (fname, checksum, url) - mods = read_file(args.version_file) - names = [mod[0] for mod in mods] - # whitelist client mods (e.g. optifine) - names += [line[0] for line in read_file(args.whitelist_file)] - - i = 0 - for mod in mods: - mod_path = os.path.join(args.pack_location, mod[0]) - i += 1 - if os.path.exists(mod_path) and os.path.isfile(mod_path) and \ - hashlib.sha1(open(mod_path, 'rb').read()).hexdigest() == mod[1]: - print("Skipping {mod[0]}, already up to date".format(mod=mod)) - else: - print('Installing {mod[0]} from {mod[2]}...'.format(mod=mod)) - print(' ({i} of {x})'.format(i=i,x=len(mods)), end='\r') - download_obj = requests.get(mod[2], stream=True) - with open(mod_path, "wb") as write_file: - shutil.copyfileobj(download_obj.raw, write_file) - print("Done!" + " " * 8) - - print() - print("Removing old mods...") - for jar in os.listdir(args.pack_location): - if jar not in names and os.path.splitext(jar)[1] == ".jar": - os.remove(os.path.join(args.pack_location, jar)) - print("Removing '{jar}'".format(jar=jar)) - - print() - print("Finished installing mods!") - - -# Using the latest urls, update downloads.txt to match and have the correct sha1 -def apply_updates(args): - if args.game_version is not None: - version = tuple(int(x) for x in args.game_version.split('.')) - else: - version = (2, 0, 0) - print("Populating version File...") - mods = read_file(args.filename) - print("Getting new versions of all mods...") - ffx = firefox() - with open(args.version_file, 'w') as f: - f.write('# Format: <jarname> <hex digested sha1> <direct download url>\n') - f.write("#VERSION " + str(VERSION + 1) + "\n") - for mod in mods: - print("Fetching {mod[0]}...".format(mod=mod)) - if 'curseforge' in mod[1]: - url = find_cdn(ffx, mod[1], version) - else: - url = requests.get(mod[1]).url - if url is None: - print('[!]Failed to fetch {mod[0]}!'.format(mod=mod)) - continue - resp = requests.get(url) - hsh = hashlib.sha1(resp.content).hexdigest() - f.write('{mod[0]} {hsh} {resp.url}\n'.format(mod=mod, hsh=hsh, resp=resp)) - ffx.close() - print() - print("Done!") - print("Updates applied to {args.version_file}".format(args=args)) - print("New pack version is " + str(VERSION + 1)) - print("[!] No mods were installed. To update your mods folder, run 'update.py install'") - -# Find if any updates are available -def check_updates(args): - if args.game_version is not None: - version = tuple(int(x) for x in args.game_version.split('.')) - else: - version = (2, 0, 0) - print("Checking for updates to version " + str(VERSION) + "...") - latest = read_file(args.filename) - old = read_file(args.version_file) - old_urls = [mod[2] for mod in old] - num_updates = 0 - - print("Checking updates...") - ffx = firefox() - - for mod in latest: - print("Checking for updates to {mod[0]}...".format(mod=mod), end="") - sys.stdout.flush() # takes care of line-buffered terminals - if 'curseforge' in mod[1]: - url = find_cdn(ffx, mod[1], version) - else: - url = requests.get(mod[1]).url - if url in old_urls: - print(" No updates") - else: - print(" Found update: " + url.split('/')[-1]) - num_updates += 1 - ffx.close() - - print("Finished checking for updates. {num} mods can be updated".format(num=num_updates)) - if num_updates >= 0: - print("Run 'python update.py apply_updates' to create a new version with these updates applied.") - - -def find_cdn(ffx, url, version): - """ - Given a mod home URL, finds the most up-to-date mod version compatible with the given game version. - Returns the direct Forge CDN download URL - """ - #TODO filter mods by forge/fabric compatibility - try: - ffx.get(url + '/files/all') - mod_versions = ffx.find_elements_by_class_name("listing")[0].find_elements_by_xpath("tbody/tr") # extract the table of files from the page - row_info = collections.namedtuple("row_info", ["type", "filename", "cdn_id", "game_version"]) # create a custom tuple because data - rows = [] - for version_entry in mod_versions: - # parse out the four fields that we use - entry_cells = version_entry.find_elements_by_tag_name("td") - release_type = entry_cells[0].text - filename = urllib.parse.quote(entry_cells[1].find_elements_by_tag_name("a")[0].text) - try: - game_version = tuple([int(x) for x in entry_cells[4].find_element_by_class_name("mr-2").text.split(".")]) # get game version and convert to tuple - except: - game_version = (0, 0, 0) - cdn_id = entry_cells[1].find_element_by_tag_name("a").get_property("href").split("/")[-1] - rows.append(row_info(release_type, filename, cdn_id, game_version)) - rows.sort(key=lambda x: x.game_version, reverse=True) - best_row = next(x for x in rows if x.game_version <= version) - - return f'https://media.forgecdn.net/files/{best_row.cdn_id[:4]}/{best_row.cdn_id[4:]}/{best_row.filename}' - - except: - import traceback; traceback.print_exc() - return None - - -def firefox(): - print("Starting Selenium...") - try: - from selenium.webdriver import Firefox - except: - print("Applying updates requires the `selenium` package") - sys.exit(0) - return Firefox() - -COMMAND_MAP = { - 'install': install, - 'apply_updates': apply_updates, - 'check_updates': check_updates, -} - -if __name__ == "__main__": - args = parser.parse_args() - - if not args.pack_location: - # initialize from config - with open("pack-location.txt", "r") as f: - args.pack_location = f.read().strip() - - if not os.path.exists(args.version_file): - print("Error: version file\"" + args.version_file + "\" does not exist.") - parser.print_help() - sys.exit(1) - if not os.path.exists(args.pack_location): - print("Error: mod folder \"" + args.pack_location + "\" does not exist.") - parser.print_help() - sys.exit(1) - elif not os.path.isdir(args.pack_location): - print("Error: mod folder \"" + args.pack_location + "\" is not actually a folder.") - parser.print_help() - sys.exit(1) - if not os.path.exists(args.whitelist_file): - print("Error: whitelist file \"" + args.whitelist_file + "\" does not exist.") - sys.exit(1) - - if not (args.command in COMMAND_MAP): - print("Error: command \"" + args.command + "\" does not exist") - parser.print_help() - sys.exit(1) - - # fetch version - with open(args.version_file) as f: - for line in f: - if line.strip().split()[0] == "#VERSION": - VERSION = int(line.strip().split()[1]) - break - # run the command - COMMAND_MAP[args.command](args) |