aboutsummaryrefslogblamecommitdiff
path: root/update.py
blob: a04df5c99d2b61d7386a7a528701b23495ee04ca (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
                      


               
         
          





                             
 








                                                                                                                                       
 

























                                                                                          
 
                                      

                                                               
                            
                                   
                                    
 
                    



                                                                          
             
                                                                                                  
                                                            
                                                    


                                                                



                                                                   
                                                                   

                                                                              
 

                                                                        
 

                                                                                         




                                                                         

                                                                                  
                                                         
                                       





                                                                                                                                                     
 
                                   



                                                                              

                                      
                                                         
                      

                                                                             

                                    


                                                                                                                                 
 




                                   
 
























                                                                                                                           
#!/usr/bin/env python3

import argparse
import textwrap
import os
import sys
import hashlib
import shutil

import requests
import colorama
from termcolor import colored

parser = argparse.ArgumentParser(
    description="A Simple Git-Based Modpack Manager",
    formatter_class=argparse.RawDescriptionHelpFormatter,
    epilog='''\
Available commands:
    install          : Downloads mods listed in downloads.txt and populates the mods folder specified in pack-location.txt
    apply_updates    : Using the latest downloads in latest-urls.txt, repopulates downloads.txt to reflect the most recent mod versions
    check_updates    : Compares downloads.txt and latest-urls.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('--url-file',
    type=str,
    default="downloads.txt",
    help="Optional custom URL file to download mods from (default: downloads.txt)")
parser.add_argument('--pack-location',
    type=str,
    help="Optional custom modpack folder location (default: read from pack-location.txt)")

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(colored("Updating pack...", 'green', attrs=['bold']))
    # (fname, checksum, url)
    mods = read_file(args.url_file)
    names = [mod[0] for mod in mods]

    for mod in mods:
        mod_path = os.path.join(args.pack_location, mod[0])
        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(colored('Installing {mod[0]} from {mod[2]}...'.format(mod=mod), attrs=['bold']))
            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!")

    print()
    print(colored("Removing old mods...", 'green', attrs=['bold']))
    
    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(colored("Removing '{jar}'".format(jar=jar), attrs=['bold']))

    print()
    print(colored("Finished installing mods!", 'green', attrs=['bold']))


# Using the latest urls, update downloads.txt to match the urls and have the correct sha1
def apply_updates(args):
    print(colored("Populating URL File...", 'green', attrs=['bold']))
    mods = read_file(args.filename)
    print(colored("Getting new versions of all mods...", attrs=['bold']))
    with open(args.url_file, 'w') as f:
        f.write('# Format: <jarname> <hex digested sha1> <direct download url>\n')
        for mod in mods:
            print("Fetching {mod[0]}...".format(mod=mod))
            resp = requests.get(mod[1])
            hsh = hashlib.sha1(resp.content).hexdigest()
            f.write('{mod[0]} {hsh} {resp.url}\n'.format(mod=mod, hsh=hsh, resp=resp))
    print()
    print("Done!")
    print(colored("Updates applied to downloads.txt", 'green', attrs=['bold']))
    print(colored("[!] No mods were installed. To update your mods folder, run 'update.py install'", 'white', 'on_red', attrs=['bold', 'underline']))

# Find if any updates are available
def check_updates(args):
    print(colored("Checking for updates to mods...", 'green', attrs=['bold']))
    latest = read_file(args.filename)
    old = read_file(args.url_file)
    old_urls = [mod[2] for mod in old]

    print(colored("Checking updates...", attrs=['bold']))
    for mod in latest:
        print('\033[2K',end="")    # ANSI code to clear line
        print("Checking for updates to {mod[0]}...".format(mod=mod),end="\r")
        resp = requests.get(mod[1])
        if not resp.url in old_urls:
            print(colored(" -> Found update for {mod[0]}: {resp.url.split('/')[-1]}".format(mod=mod, resp=resp), attrs=['bold']))
    
    print('\033[2K' + colored("Finished checking for updates!", attrs=['bold']))

COMMAND_MAP = {
    'install': install,
    'apply_updates': apply_updates,
    'check_updates': check_updates,
}

if __name__ == "__main__":
    colorama.init()
    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.pack_location):
        print(colored("Error: mod folder \"" + args.pack_location + "\" does not exist.", 'red', attrs=['bold']))
        parser.print_help()
        sys.exit(1)
    elif not os.path.isdir(args.pack_location):
        print(colored("Error: mod folder \"" + args.pack_location + "\" is not actually a folder.", 'red', attrs=['bold']))
        parser.print_help()
        sys.exit(1)
        
    if not (args.command in COMMAND_MAP):
        print(colored("Error: command \"" + args.command + "\" does not exist", 'red', attrs=['bold']))
        parser.print_help()
        sys.exit(1)
        
    # run the command
    COMMAND_MAP[args.command](args)