diff --git a/README.mdown b/README.mdown index f5fbcf5..47599d9 100644 --- a/README.mdown +++ b/README.mdown @@ -27,7 +27,7 @@ Follow the installation instructions for your `platform`: - [Windows](#windows-installation) - [Linux](#linux-installation) -On first run, the `addons.yaml` file will be created in your ESO live directory. +On first run, the `addons.text` file will be created in your ESO live directory. It will look similar to the following: ```yaml diff --git a/banana.py b/banana.py new file mode 100644 index 0000000..954db9f --- /dev/null +++ b/banana.py @@ -0,0 +1,316 @@ +from argparse import ArgumentParser +from distutils.dir_util import copy_tree +from packaging import version +from pathlib import Path +from platform import system +from shutil import rmtree, copytree, copyfileobj +from tempfile import TemporaryDirectory, NamedTemporaryFile +from zipfile import ZipFile +from urllib.request import Request, urlopen +import logging +import re + +HEADERS = { + "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:48.0) Gecko/20100101 Firefox/48.0" +} + + +def live_to_esoui(*, path: Path, esoui_uris: list): + live_name, live_version, live_path = live_parse(path) + + if not live_path: + return + + esoui_name, esoui_version, esoui_uri = None, None, None + + for _name, _version, _uri in esoui_uris: + if _name in live_name: + esoui_name, esoui_version, esoui_uri = _name, _version, _uri + break + + if live_name in _name: + esoui_name, esoui_version, esoui_uri = _name, _version, _uri + break + + if not esoui_name: + rmtree(live_path) + logging.info(f"{live_name} addon removed from: {live_path}") + return + + if esoui_version == live_version: + logging.info(f"{live_name} is already up to date.") + return + + request = Request(esoui_uri, headers=HEADERS) + response = urlopen(request) + temp_zip = NamedTemporaryFile() + copyfileobj(response, temp_zip) + temp_dir = TemporaryDirectory() + temp_path = Path(temp_dir.name) + zip_file = ZipFile(temp_zip) + zip_file.extractall(temp_path) + + rmtree(live_path) + + for each in temp_path.iterdir(): + copytree(each, live_path) + + logging.info( + f"{live_name} updated from {live_version} to {esoui_version} at {live_path}" + ) + + +def esoui_to_live(*, esoui_uris: list, live_path: Path): + for addon_name, addon_version, esoui_dowload_uri in esoui_uris: + match = None + + for each in live_path.iterdir(): + if addon_name in each.name: + match = each + break + + if each.name in addon_name: + match = each + break + + if match: + logging.debug(f"{addon_name} already installed.") + continue + + request = Request(esoui_dowload_uri, headers=HEADERS) + response = urlopen(request) + temp_zip = NamedTemporaryFile() + copyfileobj(response, temp_zip) + temp_dir = TemporaryDirectory() + temp_path = Path(temp_dir.name) + zip_file = ZipFile(temp_zip) + zip_file.extractall(temp_path) + + for each in temp_path.iterdir(): + live_dest = live_path.joinpath(each.name) + + if live_dest.exists(): + continue + + copytree(each, live_dest) + + logging.info(f"{addon_name} installed {addon_version} at {live_dest}") + + +esoui_prefix = re.compile("https://www.esoui.com/downloads/info[0-9]+\-") +esoui_version_html = re.compile('Version:\s+[^<]+') +esoui_version_split = re.compile('Version:\s+') +esoui_download = re.compile('https://cdn.esoui.com/downloads/file[^"]*') +live_version = re.compile("##\s+Version:\s+.*") +live_version_split = re.compile("##\s+Version:\s+") + + +def esoui_parse(url: str): + addon_name = esoui_prefix.split(url)[1] + addon_name = addon_name.split(".html")[0] + + request = Request(url, headers=HEADERS) + response = urlopen(request) + response_text = response.read().decode("unicode_escape") + version_line = esoui_version_html.search(response_text).group(0) + _version = esoui_version_split.split(version_line)[1] + _version = version.parse(_version) + + esoui_page_url = url.replace("info", "download").replace(".html", "") + + request = Request(esoui_page_url, headers=HEADERS) + response = urlopen(request) + response_text = response.read().decode("unicode_escape") + esoui_dowload_uri = esoui_download.search(response_text).group(0) + head_request = Request(esoui_dowload_uri, method="HEAD", headers=HEADERS) + response = urlopen(head_request) + response_text = response.read().decode("unicode_escape") + + return addon_name, _version, esoui_dowload_uri + + +def live_parse(path: Path): + if not path.is_dir(): + logging.error(f"unexpected file object {path}, ignoring") + return + + meta_file = path.joinpath(f"{path.stem}.txt") + + if not meta_file.exists(): + for meta_file in path.glob("*.txt"): + if not meta_file.stem in path.stem: + continue + + try: + with meta_file.open("r") as file_open: + meta_data = file_open.read() + except UnicodeDecodeError: + with meta_file.open("r", encoding="latin-1") as file_open: + meta_data = file_open.read() + + addon_name = meta_file.stem + result = live_version.search(meta_data) + + if result: + _version = result.group(0) + _version = live_version_split.split(_version)[1] + _version = version.parse(_version) + else: + _version = version.parse("0") + + return addon_name, _version, path + + +config_template = """https://www.esoui.com/downloads/info7-LibAddonMenu.html +https://www.esoui.com/downloads/info1245-TamrielTradeCentre.html +https://www.esoui.com/downloads/info1146-LibCustomMenu.html +""" + + +def config_new(path: Path): + path.touch(exist_ok=True) + + with path.open("w") as file_open: + file_open.write(config_template) + + +def periodical_script(): + parser = ArgumentParser( + description="Visit https://www.esoui.com/ to search for addons and their dependencies URLs. Edit addons.text in the ESO live path and add the URL for each addon for installation. " + ) + parser.add_argument("-v", "--verbose", action="count", help="verbose logging") + parser.add_argument("-l", "--log", action="store_true") + parser.add_argument("-p", "--eso_live_path") + args = parser.parse_args() + + if args.eso_live_path: + args.eso_live_path = Path(args.eso_live_path) + else: + if system() == "Windows": + args.eso_live_path = Path.home().joinpath( + "Documents\Elder Scrolls Online\live" + ) + else: + args.eso_live_path = Path.home().joinpath( + ".steam/steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live/" + ) + + if args.verbose: + level = logging.DEBUG + format = "%(asctime)s %(filename)s:%(lineno)d %(message)s" + else: + level = logging.INFO + format = "%(asctime)s %(message)s" + + if args.log: + logging.basicConfig( + level=level, + format=format, + filename=args.eso_live_path.joinpath("banana.log"), + ) + else: + logging.basicConfig( + level=level, + format=format, + ) + + logging.info(args) + + config_path = Path(args.eso_live_path).joinpath("addons.text") + + if not config_path.exists(): + config_new(config_path) + logging.info(f'addons list created at "{config_path}"') + + with config_path.open("r") as file_open: + config_current = file_open.readlines() + + config_current = filter(None, config_current) + live_path = args.eso_live_path.joinpath("AddOns") + live_path.mkdir(parents=True, exist_ok=True) + esoui_uris = list() + + for url in config_current: + esoui = esoui_parse(url) + esoui_uris.append(esoui) + + for child in live_path.iterdir(): + live_to_esoui(path=child, esoui_uris=esoui_uris) + + esoui_to_live(esoui_uris=esoui_uris, live_path=live_path) + ttc_update(live_path=live_path) + + +def ttc(): + parser = ArgumentParser(description="Tamriel Trade Centre price table updater.") + parser.add_argument("-v", "--verbose", action="count", help="verbose logging") + parser.add_argument("-l", "--log", action="store_true") + parser.add_argument("-p", "--eso_live_path") + args = parser.parse_args() + + if args.eso_live_path: + args.eso_live_path = Path(args.eso_live_path) + else: + if system() == "Windows": + args.eso_live_path = Path.home().joinpath( + "Documents\Elder Scrolls Online\live" + ) + else: + args.eso_live_path = Path.home().joinpath( + ".steam/steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live/" + ) + + if args.verbose: + level = logging.DEBUG + format = "%(asctime)s %(filename)s:%(lineno)d %(message)s" + else: + level = logging.INFO + format = "%(asctime)s %(message)s" + + if args.log: + logging.basicConfig( + level=level, + format=format, + filename=args.eso_live_path.joinpath("banana.log"), + ) + else: + logging.basicConfig( + level=level, + format=format, + ) + + logging.info(args) + + live_path = Path(args.eso_live_path).joinpath("AddOns") + + if not live_path.is_dir(): + logging.error(f"eso_live_path_invalid_dir {live_path}") + return + + ttc_update(live_path=live_path) + + +price_table_uri = "https://us.tamrieltradecentre.com/download/PriceTable" +price_table_name = "TamrielTradeCentre" + + +def ttc_update(live_path: Path): + request = Request(price_table_uri, headers=HEADERS) + response = urlopen(request) + temp_zip = NamedTemporaryFile() + copyfileobj(response, temp_zip) + temp_dir = TemporaryDirectory() + temp_path = Path(temp_dir.name) + zip_file = ZipFile(temp_zip) + zip_file.extractall(temp_path) + + live_tamriel_trade_centre = live_path.joinpath("TamrielTradeCentre") + copy_tree(str(temp_path.absolute()), str(live_tamriel_trade_centre.absolute())) + + logging.info( + f"tamriel trade centre price table updated: {live_tamriel_trade_centre}" + ) + + +if __name__ == "__main__": + periodical_script()