This commit is contained in:
Šarūnas Nejus 2026-02-01 15:46:08 +01:00 committed by GitHub
commit cd2c2a931b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 50 additions and 46 deletions

View file

@ -37,11 +37,11 @@ class IncludeLazyConfig(confuse.LazyConfig):
YAML files specified in an `include` setting.
"""
def read(self, user=True, defaults=True):
def read(self, user: bool = True, defaults: bool = True) -> None:
super().read(user, defaults)
try:
for view in self["include"]:
for view in self["include"].sequence():
self.set_file(view.as_filename())
except confuse.NotFoundError:
pass

View file

@ -37,7 +37,7 @@ from beets.util.deprecation import deprecate_for_maintainers, deprecate_for_user
if TYPE_CHECKING:
from collections.abc import Callable, Iterable, Iterator, Sequence
from confuse import ConfigView
from confuse import Subview
from beets.dbcore import Query
from beets.dbcore.db import FieldQueryType
@ -162,7 +162,7 @@ class BeetsPlugin(metaclass=BeetsPluginMeta):
album_template_fields: TFuncMap[Album]
name: str
config: ConfigView
config: Subview
early_import_stages: list[ImportStageFunc]
import_stages: list[ImportStageFunc]

View file

@ -30,7 +30,6 @@ import textwrap
import traceback
from difflib import SequenceMatcher
from functools import cache
from itertools import chain
from typing import TYPE_CHECKING, Any, Literal
import confuse
@ -551,18 +550,20 @@ def get_color_config() -> dict[ColorName, str]:
legacy single-color format. Validates all color names against known codes
and raises an error for any invalid entries.
"""
colors_by_color_name: dict[ColorName, list[str]] = {
k: (v if isinstance(v, list) else LEGACY_COLORS.get(v, [v]))
for k, v in config["ui"]["colors"].flatten().items()
}
if invalid_colors := (
set(chain.from_iterable(colors_by_color_name.values()))
- CODE_BY_COLOR.keys()
):
raise UserError(
f"Invalid color(s) in configuration: {', '.join(invalid_colors)}"
template_dict: dict[ColorName, confuse.OneOf[str | list[str]]] = {
n: confuse.OneOf(
[
confuse.Choice(sorted(LEGACY_COLORS)),
confuse.Sequence(confuse.Choice(sorted(CODE_BY_COLOR))),
]
)
for n in ColorName.__args__ # type: ignore[attr-defined]
}
template = confuse.MappingTemplate(template_dict)
colors_by_color_name = {
k: (v if isinstance(v, list) else LEGACY_COLORS.get(v, [v]))
for k, v in config["ui"]["colors"].get(template).items()
}
return {
n: ";".join(str(CODE_BY_COLOR[c]) for c in colors)

View file

@ -335,6 +335,7 @@ def _summary_judgment(rec):
summary judgment is made.
"""
action: importer.Action | None
if config["import"]["quiet"]:
if rec == Recommendation.strong:
return importer.Action.APPLY

View file

@ -355,10 +355,9 @@ class DiscogsPlugin(MetadataSourcePlugin):
style = self.format(result.data.get("styles"))
base_genre = self.format(result.data.get("genres"))
if self.config["append_style_genre"] and style:
genre = self.config["separator"].as_str().join([base_genre, style])
else:
genre = base_genre
genre = base_genre
if self.config["append_style_genre"] and genre is not None and style:
genre += f"{self.config['separator'].as_str()}{style}"
discogs_albumid = self._extract_id(result.data.get("uri"))

View file

@ -288,7 +288,8 @@ class Candidate:
elif check == ImageAction.REFORMAT:
self.path = ArtResizer.shared.reformat(
self.path,
plugin.cover_format,
# TODO: fix this gnarly logic to remove the need for type ignore
plugin.cover_format, # type: ignore[arg-type]
deinterlaced=plugin.deinterlace,
)
@ -1367,7 +1368,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
# allow both pixel and percentage-based margin specifications
self.enforce_ratio = self.config["enforce_ratio"].get(
confuse.OneOf(
confuse.OneOf[bool | str](
[
bool,
confuse.String(pattern=self.PAT_PX),

View file

@ -41,6 +41,7 @@ if TYPE_CHECKING:
import optparse
from collections.abc import Callable
from beets.importer import ImportSession, ImportTask
from beets.library import LibModel
LASTFM = pylast.LastFMNetwork(api_key=plugins.LASTFM_KEY)
@ -178,14 +179,13 @@ class LastGenrePlugin(plugins.BeetsPlugin):
"""A tuple of allowed genre sources. May contain 'track',
'album', or 'artist.'
"""
source = self.config["source"].as_choice(("track", "album", "artist"))
if source == "track":
return "track", "album", "artist"
if source == "album":
return "album", "artist"
if source == "artist":
return ("artist",)
return tuple()
return self.config["source"].as_choice(
{
"track": ("track", "album", "artist"),
"album": ("album", "artist"),
"artist": ("artist",),
}
)
# More canonicalization and general helpers.
@ -603,10 +603,8 @@ class LastGenrePlugin(plugins.BeetsPlugin):
lastgenre_cmd.func = lastgenre_func
return [lastgenre_cmd]
def imported(
self, session: library.Session, task: library.ImportTask
) -> None:
self._process(task.album if task.is_album else task.item, write=False)
def imported(self, _: ImportSession, task: ImportTask) -> None:
self._process(task.album if task.is_album else task.item, write=False) # type: ignore[attr-defined]
def _tags_for(
self,

View file

@ -358,7 +358,7 @@ class LRCLib(Backend):
for group in self.fetch_candidates(artist, title, album, length):
candidates = [evaluate_item(item) for item in group]
if item := self.pick_best_match(candidates):
lyrics = item.get_text(self.config["synced"])
lyrics = item.get_text(self.config["synced"].get(bool))
return lyrics, f"{self.GET_URL}/{item.id}"
return None

View file

@ -69,7 +69,7 @@ class PlaylistQuery(InQuery[bytes]):
relative_to = os.path.dirname(playlist_path)
else:
relative_to = config["relative_to"].as_filename()
relative_to = beets.util.bytestring_path(relative_to)
relative_to_bytes = beets.util.bytestring_path(relative_to)
for line in f:
if line[0] == "#":
@ -78,7 +78,7 @@ class PlaylistQuery(InQuery[bytes]):
paths.append(
beets.util.normpath(
os.path.join(relative_to, line.rstrip())
os.path.join(relative_to_bytes, line.rstrip())
)
)
f.close()

View file

@ -262,8 +262,9 @@ class SmartPlaylistPlugin(BeetsPlugin):
"Updating {} smart playlists...", len(self._matched_playlists)
)
playlist_dir = self.config["playlist_dir"].as_filename()
playlist_dir = bytestring_path(playlist_dir)
playlist_dir = bytestring_path(
self.config["playlist_dir"].as_filename()
)
tpl = self.config["uri_format"].get()
prefix = bytestring_path(self.config["prefix"].as_str())
relative_to = self.config["relative_to"].get()

View file

@ -104,7 +104,7 @@ class TitlecasePlugin(BeetsPlugin):
@cached_property
def replace(self) -> list[tuple[str, str]]:
return self.config["replace"].as_pairs()
return self.config["replace"].as_pairs(default_value="")
@cached_property
def the_artist(self) -> bool:

13
poetry.lock generated
View file

@ -747,18 +747,21 @@ files = [
[[package]]
name = "confuse"
version = "2.1.0"
version = "2.2.0"
description = "Painless YAML config files"
optional = false
python-versions = ">=3.9"
python-versions = ">=3.10"
files = [
{file = "confuse-2.1.0-py3-none-any.whl", hash = "sha256:502be1299aa6bf7c48f7719f56795720c073fb28550c0c7a37394366c9d30316"},
{file = "confuse-2.1.0.tar.gz", hash = "sha256:abb9674a99c7a6efaef84e2fc84403ecd2dd304503073ff76ea18ed4176e218d"},
{file = "confuse-2.2.0-py3-none-any.whl", hash = "sha256:470c6aa1a5008c8d740267f2ad574e3a715b6dd873c1e5f8778b7f7abb954722"},
{file = "confuse-2.2.0.tar.gz", hash = "sha256:35c1b53e81be125f441bee535130559c935917b26aeaa61289010cd1f55c2b9e"},
]
[package.dependencies]
pyyaml = "*"
[package.extras]
docs = ["sphinx (>=7.4.7)", "sphinx-rtd-theme (>=3.0.2)"]
[[package]]
name = "coverage"
version = "7.11.0"
@ -4583,4 +4586,4 @@ web = ["flask", "flask-cors"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<4"
content-hash = "f8ce55ae74c5e3c5d1d330582f83dae30ef963a0b8dd8c8b79f16c3bcfdb525a"
content-hash = "d00ffb911964e9943b08c5ae3de28867cd5636700bdfe4a70534b02843ea80d7"

View file

@ -44,7 +44,7 @@ Changelog = "https://github.com/beetbox/beets/blob/master/docs/changelog.rst"
python = ">=3.10,<4"
colorama = { version = "*", markers = "sys_platform == 'win32'" }
confuse = ">=2.1.0"
confuse = ">=2.2.0"
jellyfish = "*"
lap = ">=0.5.12"
mediafile = ">=0.12.0"