diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 72647f15c..388962b65 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -26,7 +26,7 @@ from __future__ import annotations from functools import singledispatchmethod from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from beets import config, library, plugins, ui from beets.library import Album, Item @@ -42,43 +42,6 @@ if TYPE_CHECKING: from beets.library import LibModel -# Canonicalization tree processing. - - -def flatten_tree( - elem: dict[Any, Any] | list[Any] | str, - path: list[str], - branches: list[list[str]], -) -> None: - """Flatten nested lists/dictionaries into lists of strings - (branches). - """ - if not path: - path = [] - - if isinstance(elem, dict): - for k, v in elem.items(): - flatten_tree(v, [*path, k], branches) - elif isinstance(elem, list): - for sub in elem: - flatten_tree(sub, path, branches) - else: - branches.append([*path, str(elem)]) - - -def find_parents(candidate: str, branches: list[list[str]]) -> list[str]: - """Find parents genre of a given genre, ordered from the closest to - the further parent. - """ - for branch in branches: - try: - idx = branch.index(candidate.lower()) - return list(reversed(branch[: idx + 1])) - except ValueError: - continue - return [candidate] - - class LastGenrePlugin(plugins.BeetsPlugin): def __init__(self) -> None: super().__init__() @@ -113,7 +76,7 @@ class LastGenrePlugin(plugins.BeetsPlugin): ) loader = DataFileLoader.from_config( - self.config, self._log, Path(__file__).parent, flatten_tree + self.config, self._log, Path(__file__).parent ) self.whitelist = loader.whitelist self.c14n_branches = loader.c14n_branches @@ -133,7 +96,7 @@ class LastGenrePlugin(plugins.BeetsPlugin): return ("artist",) return tuple() - # More canonicalization and general helpers. + # Canonicalization and filtering. def _get_depth(self, tag: str) -> int | None: """Find the depth of a tag in the genres tree.""" @@ -153,6 +116,17 @@ class LastGenrePlugin(plugins.BeetsPlugin): depth_tag_pairs.sort(reverse=True) return [p[1] for p in depth_tag_pairs] + @staticmethod + def find_parents(candidate: str, branches: list[list[str]]) -> list[str]: + """Find parent genres of a given genre, ordered from closest to furthest.""" + for branch in branches: + try: + idx = branch.index(candidate.lower()) + return list(reversed(branch[: idx + 1])) + except ValueError: + continue + return [candidate] + def _resolve_genres(self, tags: list[str]) -> list[str]: """Canonicalize, sort and filter a list of genres. @@ -185,11 +159,11 @@ class LastGenrePlugin(plugins.BeetsPlugin): if self.whitelist: parents = [ x - for x in find_parents(tag, self.c14n_branches) + for x in self.find_parents(tag, self.c14n_branches) if self._is_valid(x) ] else: - parents = [find_parents(tag, self.c14n_branches)[-1]] + parents = [self.find_parents(tag, self.c14n_branches)[-1]] tags_all += parents # Stop if we have enough tags already, unless we need to find diff --git a/beetsplug/lastgenre/loaders.py b/beetsplug/lastgenre/loaders.py index de71fb94d..3d650bd5e 100644 --- a/beetsplug/lastgenre/loaders.py +++ b/beetsplug/lastgenre/loaders.py @@ -56,7 +56,6 @@ class DataFileLoader: config: ConfigView, log: Logger, plugin_dir: Path, - flatten_func: Callable, ) -> DataFileLoader: """Create a DataFileLoader from plugin configuration. @@ -77,7 +76,6 @@ class DataFileLoader: config["canonical"].get(), default_tree, config["prefer_specific"].get(), - flatten_func, ) return cls(log, plugin_dir, whitelist, c14n_branches, canonicalize) @@ -109,7 +107,6 @@ class DataFileLoader: config_value: str | bool | None, default_path: str, prefer_specific: bool, - flatten_func: Callable, ) -> tuple[list[list[str]], bool]: """Load the canonicalization tree from a YAML file. @@ -129,5 +126,24 @@ class DataFileLoader: log.debug("Loading canonicalization tree {}", c14n_filename) with Path(c14n_filename).expanduser().open(encoding="utf-8") as f: genres_tree = yaml.safe_load(f) - flatten_func(genres_tree, [], c14n_branches) + DataFileLoader.flatten_tree(genres_tree, [], c14n_branches) return c14n_branches, canonicalize + + @staticmethod + def flatten_tree( + elem: dict | list | str, + path: list[str], + branches: list[list[str]], + ) -> None: + """Flatten nested lists/dictionaries into lists of strings (branches).""" + if not path: + path = [] + + if isinstance(elem, dict): + for k, v in elem.items(): + DataFileLoader.flatten_tree(v, [*path, k], branches) + elif isinstance(elem, list): + for sub in elem: + DataFileLoader.flatten_tree(sub, path, branches) + else: + branches.append([*path, str(elem)])