mirror of
https://github.com/beetbox/beets.git
synced 2026-02-08 08:25:23 +01:00
lastgenre: Move tree helpers to where they are used
This commit is contained in:
parent
ae7e265fd8
commit
95f1c67dae
2 changed files with 36 additions and 46 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)])
|
||||
|
|
|
|||
Loading…
Reference in a new issue