Commit Graph

14999 Commits

Author SHA1 Message Date
Šarūnas Nejus
ca8e9f69a8 typing: type beets.dbcore.sort 2026-07-15 20:23:55 +01:00
Šarūnas Nejus
7a553aad7e typing: fix beets.dbcore.db 2026-07-15 20:23:54 +01:00
Šarūnas Nejus
8d03803fcf typing: type beets.dbcore.db 2026-07-15 20:23:54 +01:00
Vinícius Martins Cotrim
6a051f9699 Recognize native Spotify URIs when extracting IDs (#6840)
### 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>
2026-07-15 21:21:25 +02:00
Šarūnas Nejus
3736513c1c lyrics: add lrcmux backend (#6816)
## Description

This PR adds https://lrcmux.dev
([repo](https://github.com/f1nniboy/lrcmux)) as a new lyrics fetcher
backend. lrcmux aggregates various different lyrics sources and exposes
them via a single API, so it's perfect as a replacement for the
currently defunct Musixmatch fetcher. Users can host their own instance
if they prefer not to share their queries with a central API.
2026-07-15 15:27:57 +01:00
f1nn
0d65a46bbc lyrics: add lrcmux backend 2026-07-15 16:12:21 +02:00
J0J0 Todos
361405a42d lastgenre: Genre spelling normalization (aliases) (#6466)
# Description

This PR introduces a regex-based **normalization (alias) system** to
unify variant genre tags and improves the plugin's documentation.

The normalization feature uses an ordered list of regular expression
aliases to map variant spellings or synonyms to a single canonical name.
The mapping keys act as `re.Match.expand()` templates, supporting
`\g<N>` (or shorter, eg. `\1`) back-references to regex capture groups:

```yaml
lastgenre:
    aliases:
        # Maps 'hip-hop' -> 'hip hop' using back-references 
        # (regex full-match)
        \1hop:
            - (hip|trip|jazz)y?[ /-]*hop
        
        # Static normalization examples
        drum and bass:
            - dnb
            - d&b
```

- **Where?**: Integrated `normalize_genre` before `is_ignored` in both
the `LastFmClient` (for clean lookup/filtering) and the core
`_resolve_genres` loop (for uniform processing of existing file tags).
- Updated default whitelist and genre tree files for canonical-name
consistency, and curated the bundled `aliases.yaml` from the top 1,000
Last.fm tags to cover common inconsistencies without over-normalizing.
- Any normalization that occurs is logged (extra debug log level `beet
-vvv ...`)
- **Additional documentation changes**: 
- Added a **[Choosing the Right
Tool](file:///Users/jtiefenbacher/git/beets/docs/plugins/lastgenre.rst)**
guide. This is for endusers but note that it might even be _a primary
starting point for maintainer review._
- Added **non-whitelist mode** details to the **Canonicalization**
chapter.
- **Additional unrelated changes**: 
- Fixes a few variable name inconsistencies in the recently merged
`ignorelist` features' tests.
- Improved naming of ignorelist related type `IgnorePatternsByArtist`
2026-07-15 16:09:37 +02:00
J0J0 Todos
c51f5755f3 lastgenre: Whitelist/Tree fixes; Default aliases
- 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>
2026-07-15 16:04:51 +02:00
J0J0 Todos
fda3fef148 lastgenre: Genre aliases docs; Tools chapter, More
- 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"
2026-07-15 16:04:51 +02:00
J0J0 Todos
3ce8c9bbf5 lastgenre: Tests for _filter_valid helper,
even though it's indirectly tested via resolve_genres tests and
elsewhere, it makes sense to keep them to help with future refactoring.
2026-07-15 16:04:51 +02:00
J0J0 Todos
1674b5e7f9 lastgenre: Tests for genre alias normalization
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-07-15 16:04:51 +02:00
J0J0 Todos
e3568273ee lastgenre: Sync instance var naming
client and main plugin classes use the same names for instance
variables now
2026-07-15 16:04:51 +02:00
J0J0 Todos
4b9fa31a5b lastgenre: Genre aliases; Refactoring
- Genre alias normalization feature implementation
- Extend and simplify _filter_valid()
- Remove drop_ignored_genres()
- More smaller ignorelist related refactoring
2026-07-15 16:04:51 +02:00
J0J0 Todos
a78c94c443 Changelog for #6466 genre aliases 2026-07-15 16:04:51 +02:00
Šarūnas Nejus
dab3bd337d ci: test base installation dependencies (#6824)
## Summary

- add a CI job that installs Beets without dependency groups or plugin
extras
- run `beet version` with an isolated configuration directory to verify
the base installation

Fixes #5713
2026-07-15 14:41:20 +01:00
Šarūnas Nejus
5616799a8e Merge branch 'master' into issues/5713-base-install-smoke-test 2026-07-15 14:36:03 +01:00
Šarūnas Nejus
b37fd0e75e Preserve null artpath in edit (#6839)
Fixes #2438.

When editing album metadata, `artpath: null` was converted through the
string parsing path and could become a path ending in `None`.

This keeps YAML null values as `None` when dumping and applying edit
data, and adds a regression test for editing album metadata while
`artpath` is missing.

- Added a regression test.
- Added a changelog entry.
2026-07-15 14:35:03 +01:00
AleksZyro
4fb451f450 Treat null edit values as safe 2026-07-15 14:30:42 +01:00
AleksZyro
bb0c0992e5 Format changelog entry 2026-07-15 14:30:42 +01:00
AleksZyro
a6d576d039 Preserve null artpath in edit 2026-07-15 14:30:42 +01:00
Šarūnas Nejus
4775599c35 Make InQuery hashable when its pattern is a list (#6838)
Fixes #5354.

`beet splupdate` raises `TypeError: unhashable type: 'list'` whenever a
smartplaylist configuration contains a `playlist:` query. smartplaylist
collects playlists in a `set`, which hashes the query. `playlist:`
parses to `PlaylistQuery`, a subclass of `InQuery` whose `pattern` is a
list, and `InQuery` inherited `FieldQuery.__hash__`, which does
`hash(self.pattern)` and fails on a list.

As @wisp3rwind suggested on the issue, this overrides `__hash__` on
`InQuery` to hash a tuple copy of the pattern. It stays order-sensitive
to match the inherited `FieldQuery.__eq__`, so the hash/eq contract
holds.

Changes:
- `beets/dbcore/query.py`: add `InQuery.__hash__`.
- `test/dbcore/test_query.py`: regression test that an `InQuery` with a
list pattern can be put in a set.
- changelog entry under Bug fixes.

Left the order-insensitive `__eq__`/`__hash__` and the smartplaylist
`set`-vs-`dict` cleanup you also floated out of scope here; happy to
follow up on either if you want them.
2026-07-15 08:07:30 +01:00
Dhruv Maniya
bdfacbfcc7 ci: use Poe for base installation 2026-07-15 08:05:05 +01:00
Dhruv Maniya
1f182de4a2 ci: test base installation dependencies 2026-07-15 08:05:05 +01:00
Valentyn Kit
eb7c832fc9 Make InQuery hashable when its pattern is a list
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.
2026-07-15 08:02:01 +01:00
Šarūnas Nejus
35e481148f missing: honor -f/--format in album mode (#6831)
Fixes #3804.

The `missing` plugin ignored `-f`/`--format` (and the `format_album`
config) in album mode. `beet missing -a` always printed a hardcoded
`<artist> - <title>` and never consulted the format string. Now each
missing release group is turned into an `Album` and emitted with
`format_album`, so the format option is respected. With the default
`$albumartist - $album` the output is unchanged, matching the behavior
already documented in `docs/plugins/missing.rst`.
2026-07-15 07:59:43 +01:00
Sebastian Cao
2ada8a947d move the changelog entry under unreleased 2026-07-15 14:32:13 +08:00
Sebastian Cao
7d4f160c36 missing: honor -f/--format in album mode 2026-07-15 14:31:58 +08:00
Šarūnas Nejus
95abdfb5a3 Improve subsonicupdate error for non-JSON responses (#6837)
Fixes #5635.

When the Subsonic server returns a non-JSON response, `subsonicupdate`
currently logs the JSON decode error directly. This can produce a
cryptic message like `Expecting value`.

This logs a clearer error with the server URL and HTTP status, and adds
a regression test for an HTML error response.
2026-07-14 19:33:46 +01:00
AleksZyro
04f2c3e794 Improve subsonicupdate server error 2026-07-14 20:09:56 +02:00
Sebastian Mohr
a85068bef8 Removed a few more unittests (#6822)
Found a few more places were it is possible to easily get rid of the
`unittest` setup in favor for `pytest`.


Touches and removes `unittest` from the following files:
- `test_ihate`
- `test_the`
- `test_substitute`
- `test_m3u`
- `test_acousticbrainz`
- `test_advancedwrite`
- `test_tidal`
- `test_inline`
- `test_mpdstats`
- `test_replace`
- `test_rewrite`

related to #5361
2026-07-14 17:28:26 +02:00
Sebastian Mohr
19f944698d rewrite: PluginTestCase -> PluginTestHelper 2026-07-14 17:24:17 +02:00
Sebastian Mohr
5364644238 replace: PluginTestCase -> PluginTestHelper 2026-07-14 17:24:17 +02:00
Sebastian Mohr
9f75017d72 mpdstats: PluginTestCase -> PluginTestHelper 2026-07-14 17:24:17 +02:00
Sebastian Mohr
a06c4b4736 inline: PluginTestCase -> PluginTestHelper 2026-07-14 17:24:17 +02:00
Sebastian Mohr
ca2acfc28a tidal: PluginTestCase -> PluginTestHelper 2026-07-14 17:24:17 +02:00
Sebastian Mohr
2b90679100 advancedrewrite: PluginTestCase -> PluginTestHelper 2026-07-14 17:24:17 +02:00
Sebastian Mohr
9f75e2b211 acousticbrainz: Removed unittest.TestCase 2026-07-14 17:24:17 +02:00
Sebastian Mohr
757af4aebd util/m3u: Removed unittest.TestCase 2026-07-14 17:24:17 +02:00
Sebastian Mohr
d19e36a0a1 substitute: PluginTestCase -> PluginTestHelper 2026-07-14 17:24:17 +02:00
Sebastian Mohr
9616065591 the: Removed unittest.TestCase 2026-07-14 17:24:17 +02:00
Sebastian Mohr
c04bae3c8f ihate: Removed unittest.TestCase 2026-07-14 17:24:17 +02:00
Šarūnas Nejus
15949c86c5 Fix incorrect line numbers in logs (#6833)
Because beets wraps `logging.Logger`, the default `stacklevel=1`
incorrectly reports all log messages being emitted from
`beets/logging.py:163` at the `super()._log()` callsite. Change the
default to `stacklevel=2` so that the reported line numbers in debug
logging are the actual logging callsites.
2026-07-14 14:51:09 +01:00
Šarūnas Nejus
658dcd17a0 Merge branch 'master' into overhacked/fix_log_line_numbers 2026-07-14 14:45:48 +01:00
Šarūnas Nejus
1a010bb6af playlist: refactor tests (#6819)
Part of #6807.

- Refactors `test/plugins/test_playlist.py` to use `pathlib.Path` as the
main path abstraction instead of building paths manually with
`os.path.join`.
- Centralizes playlist test setup by introducing shared helpers like
`write_absolute_playlist()`, `write_relative_playlist()`, and reusable
path attributes such as `c_track_path` and `nonexisting_track_path`.
- Simplifies fixture data creation by using `self.add_album(...)` in a
small loop instead of constructing test items manually.

- Architecturally, this change makes the test base class responsible for
common path definitions, playlist file generation, and initial library
setup. The individual test classes now mostly declare only the
`playlist` configuration they care about.
- High-level impact: the playlist tests become more consistent, less
repetitive, and easier to extend, while keeping behavior the same.
- It also makes path handling more explicit and type-safe in the tests
by treating filesystem paths as `Path` objects and only converting to
`str` where config values require it.
2026-07-14 14:45:19 +01:00
Šarūnas Nejus
d8442811ba Merge branch 'master' into overhacked/fix_log_line_numbers 2026-07-14 14:37:45 +01:00
Šarūnas Nejus
e671c375f1 Make music_dir a Path, simplify setup 2026-07-14 14:36:12 +01:00
Šarūnas Nejus
364d027371 Define attribute for item paths 2026-07-14 14:36:12 +01:00
Šarūnas Nejus
70174db929 Write playlists inside setUp 2026-07-14 14:36:12 +01:00
Šarūnas Nejus
ea12df17a9 Define write_relative_playlist 2026-07-14 14:36:12 +01:00
Šarūnas Nejus
6586ea82cb Define write_absolute_playlist 2026-07-14 14:36:12 +01:00