Commit Graph
4452 Commits
Author SHA1 Message Date
Šarūnas Nejus f591a02752 Update docs 2026-08-27 03:17:51 +01:00
Šarūnas Nejus c7dc31fe29 Add a changelog note and a mention in queries.rst 2026-08-27 02:56:48 +01:00
Šarūnas Nejus d9e2b65758 Merge branch 'master' into fix/lrclib-null-lyrics 2026-08-22 17:41:55 +01:00
Šarūnas Nejus a3bb963ea0 ipfs: fix play command 2026-08-20 00:51:44 +01:00
Šarūnas Nejus 0c05551251 Update events docs 2026-08-18 10:46:33 +01:00
Šarūnas Nejus cf385e7614 Merge branch 'master' into fix/skip-empty-metadata-search 2026-08-17 09:35:05 +01:00
Šarūnas Nejus 9b16f67af1 Remove bluelet in favor of using asyncio in bpd plugin 2026-08-17 05:05:21 +01:00
David Howard cd50a42c04 lyrics: don't match LRCLib entries that have no lyrics
LRCLib stores track metadata independently of the lyrics themselves, so an
entry can come back with both `plainLyrics` and `syncedLyrics` null while
`instrumental` is False. `LRCLyrics.is_valid` accepted such an entry as a
match on duration alone, and `get_text` then returned `self.plain`, i.e.
None, despite being annotated `-> str`.

The None propagated into `Lyrics`, and the first access of its text raised

    AttributeError: 'NoneType' object has no attribute 'splitlines'

`beet lyrics` surfaced this per track, but during an import the exception
escaped the pipeline stage and aborted the entire run: one such track
stranded every file queued behind it.

Treat an entry with no lyrics text as not a match, so the search moves on to
other candidates and backends and ultimately reports that no lyrics were
found. That is deliberately distinct from an instrumental track, where "no
lyrics" is itself the answer and the existing `instrumental` handling still
applies. Marking these as instrumental would assert something the API
response does not tell us.

Also fall back to synced lyrics when only `plainLyrics` is null, rather than
discarding lyrics we do have, and correct the annotations: `plainLyrics` and
`LRCLyrics.plain` are both nullable.
2026-08-15 17:49:54 -07:00
Sanjay Santhanam 127a2d8914 docs: keep changelog entry under unreleased 2026-08-15 16:59:28 -07:00
Henry b7993d19ac fix(discogs): retry malformed search responses #6912 2026-08-15 09:53:26 -07:00
Cohen Karnell d97d78f830 Fix KeyError in Deezer track conversion when artist is missing
_get_track resolved the artist with track_data.get('contributors', [track_data['artist']]),
whose default is evaluated eagerly, so track_data['artist'] raises KeyError whenever the
artist key is absent, even when contributors is present. Guard the fallback the same way
album_for_id already does. Fixes #4339.
2026-08-14 12:02:07 -04:00
Sebastian Mohr 54b5d41acd Merge branch 'master' into fix/skip-empty-metadata-search 2026-08-12 12:28:20 +02:00
Trey Turner afd07ce5d3 fix(discogs): retry malformed search responses 2026-08-11 16:04:08 -05:00
Alok Saboo abacc3aad0 Merge branch 'master' into upgrade2 2026-08-11 08:19:15 -04:00
NoDancing f6b7e1f761 tidal: Normalize copyright text into a concise label name
_parse_label() used to store Tidal's raw copyright info as the label
name. Add _normalize_label(), a helper that strips leading markers,
years, legal entities, and boilerplate clauses.

Fixes #6796
2026-08-11 00:39:31 +01:00
Sanjay Santhanam 4596bd21d2 docs: reformat changelog entry with docstrfmt 2026-08-06 10:06:50 -07:00
Sanjay Santhanam 0ab54bd44e Simplify empty-search comment and shorten changelog entry 2026-08-06 10:04:57 -07:00
Sanjay Santhanam 6fff0532d6 docs: scope changelog entry to SearchApiMetadataSourcePlugin plugins 2026-08-06 10:04:57 -07:00
Sanjay Santhanam 58bd7ede4c fix: skip metadata source search when query and filters are empty
Items with no artist or title tags produce a search with an empty query
and no filters. The request was still sent to the metadata source API,
and MusicBrainz answers it with a 400 Bad Request, which was logged with
a traceback once per affected file during an import.

Return no candidates instead of issuing a request that cannot match
anything.
2026-08-06 10:04:57 -07:00
Alok Saboo 1ddc95f7a6 address copilot comments 2026-08-05 09:05:51 -04:00
Alok Saboo cbe300f7ef Merge branch 'master' into upgrade2 2026-08-05 08:57:42 -04:00
Alok Saboo c19b6b42d8 add interactive upgrade option 2026-08-05 08:47:26 -04:00
Šarūnas Nejus 03045b80e9 Merge branch 'master' into master 2026-08-04 16:45:30 +01:00
Vincent Gao 2fe5dc7a0e Fix storing flex media fields during update 2026-08-04 08:29:44 +02:00
Sebastian Cao c7a842941d Fix queries for flexible attributes with uppercase names 2026-08-03 20:31:12 +08:00
Mohsin Hirani 205ededdce docs: move changelog entry to Unreleased section 2026-08-02 13:49:13 -04:00
Mohsin Hirani 503830cacb docs: apply docstrfmt formatting to editor section 2026-08-02 13:45:06 -04:00
Mohsin Hirani a849afb4c1 docs: add editor config option to configuration reference 2026-08-02 13:36:14 -04:00
Mohsin Hirani 441d8f5a96 docs: apply docstrfmt formatting to changelog 2026-08-02 13:35:16 -04:00
Mohsin Hirani 1e88e48e78 docs: add changelog entry for editor config option 2026-08-02 13:34:52 -04:00
Piotr Szpetkowski 681f27feb6 fix(deezer): use free text for singleton searches
Singleton searches built the query as `<title> artist:"<artist>"`. Deezer
discards unquoted free text as soon as a query contains any field:"value"
filter, so that was evaluated as `artist:"<artist>"` alone - every track by
the artist, in Deezer's own relevance order, truncated to `search_limit`
(default 5). Substituting nonsense for the title returns a byte-identical
result set. For any artist with more releases than that window, the track
being imported was simply never among the candidates offered.

Filtering on the title as well (`track:"<title>" artist:"<artist>"`) is not
a fix: `artist:` matches loosely enough to return unrelated artists, so the
two filters can intersect to nothing even for a correctly tagged file.
`track:"Get Lucky" artist:"Daft Punk"` returns zero results, while the plain
free text `Get Lucky Daft Punk` returns the right track first.

Measured over 12 tracks at the default `search_limit`, counting the wanted
track appearing anywhere in the results:

  `<title> artist:"..."`              6/12, mean rank 1.50
  `track:"..." artist:"..."`          7/12, mean rank 1.00
  free text                          10/12, mean rank 1.00

Album searches are unchanged; `album:"<name>"` has no equivalent problem.

Adds test/plugins/test_deezer.py, which did not exist.
2026-07-30 21:18:59 +02:00
Alok Saboo 7a49cc4882 Merge branch 'master' into upgrade 2026-07-30 09:11:46 -04:00
Sebastian Mohr f421079eaf Moved changelog entry into unreleased section. 2026-07-30 13:05:31 +02:00
Sai Asish Y 9667d0fcf7 lyrics: treat missing lyrics body as empty text instead of crashing
Signed-off-by: Sai Asish Y <say.apm35@gmail.com>
2026-07-30 13:04:58 +02:00
Sebastian Mohr b5019fb180 Moved changelog entry into unreleased section. 2026-07-30 12:58:31 +02:00
chuenchen309andClaude Opus 4.8 8518a13f3d docs: reflow changelog entry to satisfy docstrfmt
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 12:58:16 +02:00
chuenchen309 69772cdc6d fix(autotag): "featuring" pattern in string_dist matches mid-word "ft"
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.
2026-07-30 12:58:16 +02:00
Sebastian Mohr 5d2d3993a7 Moved changelog entry to the current unreleased section. 2026-07-30 12:53:21 +02:00
Qalipso a8439e2d07 Fix unique_path counter for names ending in two or more digits
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.
2026-07-30 12:52:03 +02:00
TowyTowyandClaude 348b87dd3e Normalise reversed date ranges instead of crashing
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>
2026-07-30 12:36:04 +02:00
Alok Saboo c44f517edf Merge branch 'master' into upgrade 2026-07-29 12:16:14 -04:00
snejus b795229994 Increment version to 2.13.1 2026-07-29 10:44:53 +00:00
Sebastian Mohr 0234e38f78 Removed unused doc sections. Preparing for hotfix. 2026-07-29 12:05:52 +02:00
Sebastian Mohr 87a07553fd Changelog entry 2026-07-29 12:05:52 +02:00
Alok Saboo 9a5d00ceda update changelog 2026-07-28 19:52:45 -04:00
Alok Saboo 6fb5e62e40 Resolve upgrade target per-album when duplicates span multiple albums 2026-07-28 19:51:30 -04:00
Alok Saboo 789c13e4f7 Add bitrate-based upgrade duplicate_action 2026-07-28 19:51:30 -04:00
Alok Saboo a486f92693 reformat beatport/bpsync docs 2026-07-28 15:28:32 -04:00
snejus 48d44d15ed Increment version to 2.13.0 2026-07-27 19:07:11 +00:00
Alok Saboo d392729b64 Merge branch 'master' into spotify 2026-07-24 10:14:31 -04:00