lastgenre: Move tree helpers to where they are used

This commit is contained in:
J0J0 Todos 2026-02-07 20:06:01 +01:00
parent ae7e265fd8
commit 95f1c67dae
2 changed files with 36 additions and 46 deletions

View file

@ -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

View file

@ -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)])