Improve AddOn detection by allowing renaming of top-level directory according to containing .toc.
This commit is contained in:
parent
1445f7d26e
commit
bedea2f983
|
@ -6,7 +6,7 @@ import re
|
|||
import shutil
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Iterable, List
|
||||
from typing import Iterable, List, Dict
|
||||
|
||||
from . import config, providers
|
||||
|
||||
|
@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
|
|||
class Addon:
|
||||
name: str = None
|
||||
url: str = None
|
||||
dirs: List[Path] = field(default_factory=list)
|
||||
dirs: Dict[str, Path] = field(default_factory=dict)
|
||||
provider: str = None
|
||||
provider_data: dict = field(default_factory=dict)
|
||||
|
||||
|
@ -26,7 +26,7 @@ class Addon:
|
|||
return {
|
||||
"name": self.name,
|
||||
"url": self.url,
|
||||
"dirs": [str(d) for d in self.dirs],
|
||||
"dirs": {name: str(path) for name, path in self.dirs.items()},
|
||||
"provider": self.provider,
|
||||
"provider_data": self.provider_data
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class Addon:
|
|||
return cls(
|
||||
name=data["name"],
|
||||
url=data["url"],
|
||||
dirs=[Path(d) for d in data["dirs"]],
|
||||
dirs={name: Path(path) for name, path in data["dirs"].items()},
|
||||
provider=data["provider"],
|
||||
provider_data=data["provider_data"]
|
||||
)
|
||||
|
@ -51,7 +51,7 @@ class Addon:
|
|||
self.download_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def is_cached(self) -> bool:
|
||||
return self.dirs and all(d.is_dir() for d in self.dirs)
|
||||
return self.dirs and all(d.is_dir() for d in self.dirs.values())
|
||||
|
||||
def get_provider(self) -> providers.Provider:
|
||||
if self.provider is not None:
|
||||
|
@ -74,36 +74,41 @@ class Addon:
|
|||
def install(self) -> None:
|
||||
logger.info("Installing %s", self)
|
||||
self.dirs = find_addon_dirs(self.download_dir)
|
||||
for dir in self.dirs:
|
||||
shutil.copytree(str(dir), config.ADDONS_DIR.joinpath(dir.name))
|
||||
for dir_name, path in self.dirs.items():
|
||||
shutil.copytree(str(path), config.ADDONS_DIR.joinpath(dir_name))
|
||||
|
||||
if self.name is None: # didn't get AddOn name from provider
|
||||
if len(self.dirs) == 1:
|
||||
self.name = self.dirs[0].name
|
||||
self.name = next(iter(self.dirs.keys()))
|
||||
else:
|
||||
print(f"Unable to identify AddOn name for {self.url}")
|
||||
self.name = input("AddOn name: ")
|
||||
|
||||
def uninstall(self) -> None:
|
||||
logger.info("Uninstalling %s", self)
|
||||
for dir in self.dirs:
|
||||
shutil.rmtree(config.ADDONS_DIR.joinpath(dir.name), ignore_errors=True)
|
||||
for dir_name in self.dirs.keys():
|
||||
shutil.rmtree(config.ADDONS_DIR.joinpath(dir_name), ignore_errors=True)
|
||||
|
||||
|
||||
def find_addon_dirs(path: Path) -> List[Path]:
|
||||
def find_addon_dirs(path: Path) -> Dict[str, Path]:
|
||||
"""
|
||||
Find and return AddOns in the given path.
|
||||
|
||||
Each WoW AddOn is identified by a .toc-file whose filename matches its containing folder. The algorithm traverses
|
||||
the path in a breadth-first fashion, returning all found AddOns in the first level which contains any AddOns.
|
||||
This ensures we find AddOns 'Foo' and 'Foo_Config'.
|
||||
WoW AddOns are identified by a .toc file whose filename matches its containing folder. The algorithm traverses the
|
||||
given directory path in a breadth-first fashion, returning all AddOns in the first level which contains any such
|
||||
directory/.toc-pairs. This ensures that we, as an example, find both AddOns 'Foo' and 'Foo_Config'.
|
||||
Occasionally, the downloaded directory will contain no directory/.toc-pairs which conform to the Blizzard AddOn
|
||||
standard; e.g. in the case of 'foo-master' from git with 'Foo.toc' in the root. Therefore, as a special case, if the
|
||||
first level in the given path contains a single .toc file, this path will be returned along with its proper name, so
|
||||
it can be renamed on install to Interface/AddOns.
|
||||
"""
|
||||
level = [path]
|
||||
while level:
|
||||
addon_dirs = []
|
||||
addon_dirs = {}
|
||||
for p in level:
|
||||
if any(toc.stem == p.name for toc in p.glob("*.toc")):
|
||||
addon_dirs.append(p)
|
||||
tocs = list(p.glob("*.toc"))
|
||||
if any(toc.stem == p.name for toc in tocs):
|
||||
addon_dirs[p.name] = p
|
||||
elif len(level) == 1 and len(tocs) == 1:
|
||||
addon_dirs[tocs[0].stem] = p
|
||||
if addon_dirs:
|
||||
logger.debug("Found AddOn dirs: %s in '%s'", addon_dirs, path)
|
||||
return addon_dirs
|
||||
|
@ -126,7 +131,7 @@ def load_installed_addons() -> List[Addon]:
|
|||
logger.debug("Loading list of installed AddOns from %s", config.CONFIG_FILE)
|
||||
try:
|
||||
with config.CONFIG_FILE.open() as cf:
|
||||
return [Addon.from_json(a) for a in json.load(cf)["installed_addons"]]
|
||||
return [Addon.from_json(addon) for addon in json.load(cf)["installed_addons"]]
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
|
||||
|
@ -134,4 +139,4 @@ def load_installed_addons() -> List[Addon]:
|
|||
def save_installed_addons(addons: Iterable[Addon]) -> None:
|
||||
logger.debug("Saving list of installed AddOns to %s", config.CONFIG_FILE)
|
||||
with config.CONFIG_FILE.open("w") as cf:
|
||||
json.dump(dict(installed_addons=[a.to_json for a in addons]), cf)
|
||||
json.dump(dict(installed_addons=[addon.to_json for addon in addons]), cf)
|
||||
|
|
|
@ -141,7 +141,7 @@ class CLI:
|
|||
"Name": addon.name,
|
||||
"Provider": addon.get_provider().__name__,
|
||||
"URL": addon.url,
|
||||
"Directories": textwrap.fill(", ".join(d.name for d in addon.dirs), width=40)
|
||||
"Directories": textwrap.fill(", ".join(addon.dirs.keys()), width=40)
|
||||
})
|
||||
from tabulate import tabulate
|
||||
print(tabulate(table, headers="keys", tablefmt="orgtbl"))
|
||||
|
|
Loading…
Reference in a new issue