The beets-usertag plugin was originally created by igordetigor, but has
been unmaintained for some time now. Recently, I decided to add some
features to it and, with Ingo's blessing, publish on PyPI. He's also
okay with me replacing the link in the plugins list with my fork.
Looks like our hard-coded API credentials expired. This PR removes
expired credentials and makes it clear to the user that they must
provide their credentials.
Fixes#6270
Fetchart colors were broken by 67e668d81f.
Since that commit fetchart displays this, with escape codes instead of
colorized output:
```
fetchart: Alasdair Fraser & Natalie Haas - Meridians: �[37mhas album art�[39;49;00m
```
This fixes it by using `ui.print_` instead of `Logger`. This seems to be
in line with other plugins such as play:
1a899cc92a/beetsplug/play.py (L135)
## Summary
This PR updates the `ftintitle` plugin to insert featured artist tokens
before brackets containing remix/edit-related keywords (e.g., "Remix",
"Live", "Edit") instead of always appending them at the end of the
title.
## Motivation
Previously, the plugin would always append featured artists at the end
of titles, resulting in awkward formatting like:
- `Threshold (Myselor Remix) ft. Hallucinator`
With this change, featured artists are inserted before the first bracket
containing keywords, producing cleaner formatting:
- `Threshold ft. Hallucinator (Myselor Remix)`
## Changes
### Core Functionality
- Added `find_bracket_position()` function that:
- Searches for brackets containing remix/edit-related keywords
- Supports multiple bracket types: `()`, `[]`, `<>`, `{}`
- Only matches brackets with matching opening/closing pairs
- Uses case-insensitive word-boundary matching for keywords
- Returns the position of the earliest matching bracket
- Updated `update_metadata()` to insert featured artists before brackets
instead of appending
### Configuration
- Added new `bracket_keywords` configuration option:
- **Default**: List of keywords including: `abridged`, `acapella`,
`club`, `demo`, `edit`, `edition`, `extended`, `instrumental`, `live`,
`mix`, `radio`, `release`, `remaster`, `remastered`, `remix`, `rmx`,
`unabridged`, `unreleased`, `version`, and `vip`
- **Customizable**: Users can override with their own keyword list
- **Empty list**: Setting to `[]` matches any bracket content regardless
of keywords
### Example Configuration
```yaml
ftintitle:
bracket_keywords: ["remix", "live", "edit", "version", "extended"]
```
## Behavior
- **Titles with keyword brackets**: Featured artists are inserted before
the first bracket containing keywords
- `Song (Remix) ft. Artist` → `Song ft. Artist (Remix)`
- `Song (Live) [Remix] ft. Artist` → `Song ft. Artist (Live) [Remix]`
(picks first bracket with keyword)
- **Titles without keyword brackets**: Featured artists are appended at
the end (backward compatible)
- `Song (Arbitrary) ft. Artist` → `Song (Arbitrary) ft. Artist`
- **Nested brackets**: Correctly handles nested brackets of same and
different types
- `Song (Remix [Extended]) ft. Artist` → `Song ft. Artist (Remix
[Extended])`
- **Multiple brackets**: Picks the earliest bracket containing keywords
- `Song (Live) (Remix) ft. Artist` → `Song ft. Artist (Live) (Remix)`
(if both contain keywords, picks first)
## Testing
- Added comprehensive test coverage for:
- Different bracket types (`()`, `[]`, `<>`, `{}`)
- Nested brackets (same and different types)
- Multiple brackets
- Custom keywords
- Empty keyword list behavior
- Edge cases (unmatched brackets, no brackets, etc.)
All 112 tests pass.
## Backward Compatibility
This change is **backward compatible**:
- Titles without brackets continue to append featured artists at the end
- Titles with brackets that don't contain keywords also append at the
end
- Existing configuration files continue to work (uses sensible defaults)
## Documentation
- Updated changelog with detailed description of the new feature
- Configuration option is documented in the changelog entry
Often last.fm does not find artist genres for delimiter-separated artist
names (eg. "Artist One, Artist Two") or where multiple artists are
combined with "concatenation words" like "and" , "+", "featuring" and so
on.
This fix gathers each artist's last.fm genre separately by using Beets'
mutli-valued `albumartists` field to improve the likeliness of finding
genres in the artist genre fetching stage.
Refactoring was done along the existing genre fetching helper functions
(`fetch_album_genre`, `fetch_track_genre`, ...):
- last.fm can be asked for genre for these combinations of metadata:
- albumartist/album
- artist/track
- artist
- Instead of passing `Album` or `Item` objects directly to these
helpers., generalize them and pass the (string) metadata directly.
- Passing "what's to fetch" in the callers instead of hiding it in the
methods also helps readability in `_get_genre()`
- And reduces the requirement at hand for another additional method (or
adaptation) to support "multi-albumartist genre fetching"
In case the albumartist genre can't be found (often due to variations of
artist-combination wording issues, eg "featuring", "+", "&" and so on)
use the albumartists list field, fetch a genre for each artist
separately and concatenate them.
Reduce fetcher methods to 3: last.fm can be asked for
for a genre for these combinations of metadata:
- albumartist/album
- artist/track
- artist
Passing them in the callers instead of hiding it in the
methods also helps readability in _get_genre().