banana/banana.py

332 lines
9.6 KiB
Python

from argparse import ArgumentParser
from io import BytesIO
from packaging import version
from pathlib import Path
from pathlib import Path
from platform import system
from shutil import rmtree, copytree
from tempfile import TemporaryDirectory
from typing import Tuple
from zipfile import ZipFile
import logging
import re
import requests
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 live_to_esoui(*, path: Path, esoui_uris: list):
live_name, live_version, live_path = parsing_live(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
response = requests.get(esoui_uri)
response.raise_for_status()
temp_dir = TemporaryDirectory()
temp_path = Path(temp_dir.name)
zip_file = ZipFile(BytesIO(response.content))
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
response = requests.get(esoui_dowload_uri)
response.raise_for_status()
temp_dir = TemporaryDirectory()
temp_path = Path(temp_dir.name)
zip_file = ZipFile(BytesIO(response.content))
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('<div\s+id="version">Version:\s+[^<]+')
esoui_version_split = re.compile('<div\s+id="version">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) -> Tuple[str, version.Version, str]:
addon_name = esoui_prefix.split(url)[1]
addon_name = addon_name.split(".html")[0]
response = requests.get(url)
response.raise_for_status()
version_line = esoui_version_html.search(response.text).group(0)
_version = esoui_version_split.split(version_line)[1]
try:
_version = version.parse(_version)
except version.InvalidVersion:
_version = version.parse("1")
return
esoui_page_url = url.replace("info", "download").replace(".html", "")
response = requests.get(esoui_page_url)
response.raise_for_status()
esoui_dowload_uri = esoui_download.search(response.text).group(0)
response = requests.head(esoui_dowload_uri)
response.raise_for_status()
return addon_name, _version, esoui_dowload_uri
def parsing_live(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]
try:
_version = version.parse(_version)
except version.InvalidVersion:
_version = version.parse("0")
else:
_version = version.parse("0")
return addon_name, _version, path
def eso_live_path_get():
if system() == "Windows":
eso_live_path = Path.home().joinpath(
"Documents\Elder Scrolls Online\live"
)
if eso_live_path.exists:
return eso_live_path
else:
eso_live_path = Path.home().joinpath(
".steam/steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live/"
)
if eso_live_path.exists():
return eso_live_path
eso_live_path = Path.home().joinpath(
".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live"
)
if eso_live_path.exists():
return eso_live_path
raise Exception("Unable to find `steamuser/Documents/Elder Scrolls Online/live`, specify the full path and contact maintainer to see if they'll add it.")
def periodical_script():
parser = ArgumentParser(
description="Visit https://www.esoui.com/ to search for addons and their dependencies URLs. Edit addons.yaml 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:
args.eso_live_path = eso_live_path_get()
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.list")
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 = [line.rstrip('\n') for line in file_open]
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)
if esoui:
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:
args.eso_live_path = eso_live_path_get()
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):
response = requests.get(price_table_uri)
response.raise_for_status()
temp_dir = TemporaryDirectory()
temp_path = Path(temp_dir.name)
zip_file = ZipFile(BytesIO(response.content))
zip_file.extractall(temp_path)
live_tamriel_trade_centre = live_path.joinpath("TamrielTradeCentre")
copytree(temp_path.absolute(), live_tamriel_trade_centre.absolute(), dirs_exist_ok=True)
logging.info(
f"tamriel trade centre price table updated: {live_tamriel_trade_centre}"
)
if __name__ == "__main__":
periodical_script()