import yaml import json import os from datetime import datetime from time import time from sys import argv from os.path import getmtime from zipfile import ZipFile, ZIP_DEFLATED DEFAULTS = { 'IsHide': False, 'IsTestingExclusive': False, 'ApplicableVersion': 'any', } DUPLICATES = { 'DownloadLinkInstall': ['DownloadLinkTesting', 'DownloadLinkUpdate'], } TRIMMED_KEYS = [ 'Author', 'Name', 'Description', 'InternalName', 'AssemblyVersion', 'RepoUrl', 'ApplicableVersion', 'Tags', 'DalamudApiLevel', 'Punchline', 'IconUrl', 'ImageUrls', ] def main(): plugins_dir = argv[1] if len(argv) > 1 else 'plugins' master_name = 'pluginmaster.json' if plugins_dir == 'plugins' else f'{plugins_dir}.json' info = { 'plugins_dir': plugins_dir, 'master_name': master_name, } # extract the manifests from inside the zip files master = extract_manifests(info) # trim the manifests master = [trim_manifest(manifest) for manifest in master] # convert the list of manifests into a master list add_extra_fields(info, master) # write the master write_master(info, master) # update the LastUpdate field in master last_update(info) def extract_manifests(info): manifests = [] for dirpath, dirnames, filenames in os.walk(f'./{info["plugins_dir"]}'): if len(filenames) == 0 or 'latest.zip' not in filenames: continue plugin_name = dirpath.split('/')[-1] latest_zip = f'{dirpath}/latest.zip' with ZipFile(latest_zip) as z: manifest = json.loads(z.read(f'{plugin_name}.json').decode('utf-8')) manifests.append(manifest) return manifests def add_extra_fields(info, manifests): for manifest in manifests: # generate the download link from the internal assembly name manifest['DownloadLinkInstall'] = f'https://git.anna.lgbt/anna/plugin_repo/raw/branch/main/{info["plugins_dir"]}/{manifest["InternalName"]}/latest.zip' # add default values if missing for k, v in DEFAULTS.items(): if k not in manifest: manifest[k] = v # duplicate keys as specified in DUPLICATES for source, keys in DUPLICATES.items(): for k in keys: if k not in manifest: manifest[k] = manifest[source] def write_master(info, master): # write as pretty json with open(info['master_name'], 'w') as f: json.dump(master, f, indent=4) def trim_manifest(plugin): return {k: plugin[k] for k in TRIMMED_KEYS if k in plugin} def last_update(info): with open(info['master_name']) as f: master = json.load(f) for plugin in master: latest = f'{info["plugins_dir"]}/{plugin["InternalName"]}/latest.zip' modified = 0 with ZipFile(latest) as z: for zip_info in z.infolist(): info_mod = datetime(*zip_info.date_time).timestamp() if info_mod > modified: modified = int(info_mod) if modified == 0: modified = int(getmtime(latest)) if 'LastUpdate' not in plugin or modified != int(plugin['LastUpdate']): plugin['LastUpdate'] = str(modified) with open(info['master_name'], 'w') as f: json.dump(master, f, indent=4) if __name__ == '__main__': main()