For a flexible attribute query, replace the `col_name` property with
a function call that extracts that attribute from the `field_attrs`
field introduced in the earlier commit.
Additionally, for boolean, numeric and date queries CAST the value to
NUMERIC SQLite affinity to ensure that our queries like 'flex:1..5' and
'flex:true' continue working fine.
This removes the concept of 'slow query', since every query for any
field now has an SQL clause.
- Track active notification tasks by connection so repeated dispatches do not
send duplicate idle responses while a previous send is still draining.
- Add regression coverage for serialized notification delivery and idle command
disconnect handling.
Return an empty string rather than INSTRUMENTAL_LYRICS from the final
fallback in LRCLyrics.get_text. That branch is not about an instrumental
track, so the marker was misleading.
Strip the LRC timestamps when synced lyrics stand in for a null
plainLyrics. The value is being used as plain text there, so the
timestamps do not belong in it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
_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.
_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
- Handle formats as strings ONLY and evaluate them through a shared
cached template helper.
- This removes the need for conditional logic that acts on Template
objects or strings.
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.
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.
- Fixes some inconsistencies in spelling in the data files we ship with
the plugin as well as adds some new genres to both files that seem to
be returned often by last.fm.
- For some changes the default aliases feature helps to streamline
inconsistencies that come from last.fm already.
- The default aliases file that ships with beets
- Tests fixes that prove that most of the alias normalization patterns
work as they are supposed to.
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>