SD_PATTERNS' featuring/feat/ft regex has no word boundary before the
alternation, so it matches "ft" embedded in ordinary words like
"draft", "left", "gift", "craft" and treats everything after it as a
low-weight suffix. "Draft Beer" vs. "Draft Whiskey" scores 0.05
(near-identical) instead of correctly registering as very different.
unique_path parses a trailing counter so it can continue from it, but the
pattern is `\.(\d)+$` -- a single-digit group repeated, not a multi-digit
group. group(1) is therefore the last digit only, and the counter restarts
from it:
track.10.mp3 -> track.1.mp3 (expected track.11.mp3)
track.12.mp3 -> track.3.mp3
track.123.mp3 -> track.4.mp3
The returned path still does not exist, so nothing is overwritten, but the
new name sorts before the file it was derived from, and the scan restarts
from a low number when the neighbours are already taken.
Use `\.(\d+)$`. Existing coverage only exercised a single-digit counter
(`x.1.mp3`), which is why this held.
A date range query whose start lies after its end, such as
`beet ls added:2024..2020`, crashed with an uncaught ValueError
raised by DateInterval's endpoint-order check:
ValueError: start date 2024-01-01 00:00:00 is not before end date
2021-01-01 00:00:00
Only InvalidQueryArgumentValueError is converted into a user-facing
InvalidQueryError by Library._get_results, so the plain ValueError
escaped all the way up as a traceback.
Rather than reporting an error, accept the range and swap its two
endpoints, so `added:2024..2020` means the same as `added:2020..2024`.
The swap lives in `DateInterval.from_periods`, which is the only place
where the two user-supplied endpoints are still available as `Period`
objects. Swapping further down, in `DateInterval.__init__`, would swap
the already-derived datetimes, and those are derived asymmetrically:
the start contributes `Period.date` while the end contributes
`Period.open_right_endpoint()`. For `2024..2020` that would yield
[2021-01-01, 2024-01-01), which excludes both 2020 and 2024 -- not the
interval the user asked for. Swapping the periods first yields
[2020-01-01, 2025-01-01), which is exactly `2020..2024`.
`DateInterval.__init__` keeps raising ValueError, since it remains the
invariant guard for callers constructing an interval from datetimes
directly, where there is no notion of the order the user typed.
The swap only triggers when the interval would otherwise be empty, so
partially overlapping mixed-precision ranges that are valid today, such
as `2000-06..2000`, are unaffected. Genuinely malformed input such as
`added:notadate` still raises InvalidQueryArgumentValueError from
`Period.parse` as before.
Co-Authored-By: Claude <noreply@anthropic.com>
## Description
Fixes#2848.
The classic Windows console (`cmd.exe`) doesn't render beets' output
(special characters, colors) correctly. Added a note in the Windows
installation guide pointing people at a terminal emulator like Windows
Terminal or cmder, or a Unix-like shell such as Git Bash.
Add a MetaflacBackend that computes ReplayGain for FLAC files with metaflac --add-replay-gain and reads the values back with --show-tag. Only FLAC is supported. Includes docs, a changelog entry, and tests.
The relative-date regex used character classes [+|-] and [y|m|w|d],
which mistakenly treat | as a literal member rather than alternation.
As a result a value like added:2000|2001 (as a user might type expecting
| to mean "or") captured | as the relative-date unit and crashed with
an uncaught KeyError in Period.parse instead of raising the documented
InvalidQueryArgumentValueError.
Remove the stray | from both character classes so malformed date strings
fall through to the normal invalid-date error path.
Co-Authored-By: Claude <noreply@anthropic.com>
### Description
`extract_release_id` now accepts the native Spotify URI format
(`spotify:album:<id>`, `spotify:track:<id>`) in addition to the already
supported `open.spotify.com` URLs and bare IDs.
Co-authored-by: Marina Pena Malschitzky Crespo <marinacrespo@estudante.ufscar.br>
- Document the genre alias normalization feature and the default aliases
we ship with the plugin (by using rst "literalinclude" referring to
aliases.yaml directly)
- Clarify what canonicalization acutually does when used without a
whitelist enabled.
- New docs chapter "Using the right tool"
InQuery inherited FieldQuery.__hash__, which hashes the pattern directly.
A list pattern raises `unhashable type: 'list'`, which broke the
smartplaylist `splupdate` command whenever a `playlist:` query was configured.
Sorting by a field whose declared type is nullable (its null value is
`None`, e.g. `NullInteger`/`NullFloat`) raised `TypeError: '<' not
supported between instances of ...` when the field was present on some
objects but missing on others, because `FieldSort.sort` compared `None`
against real values.
Group missing values together in the sort key so they are never compared
against present ones: they are ordered first when sorting ascending and
last when descending, matching SQLite's default ordering of NULLs used by
the fast `FixedFieldSort` path.
Fixes#3461.
Co-Authored-By: Claude <noreply@anthropic.com>
Enable fetching art for imports even when as-is is selected as the
metadata source. Allows for the case when files with good metadata
but without album art are being imported (e.g. digital download store).
Signed-off-by: Ross Williams <ross@ross-williams.net>
When `formats` includes `link`, importfeeds creates a symlink per imported
item. A failed symlink (lacking privilege on Windows, a read-only directory, or
a filesystem without symlink support) raised beets.util.FilesystemError out of
the import pipeline and aborted the whole `beet import` run, even though the
tracks were already imported.
Catch FilesystemError around the link() call, log a per-item warning, and
continue with the remaining items. Add regression tests for the
warn-and-continue behaviour, that only FilesystemError is caught, and that a
successful link still creates the symlink. Add a changelog entry.
Fixes#840.