Commit graph

13775 commits

Author SHA1 Message Date
Pierre Ayoub
074f4d4281 chore(changelog): Foramt with docstrfmt 2026-03-13 19:17:56 +00:00
Pierre Ayoub
fae242249a Update docs/changelog.rst
Rephrasing by Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-13 19:17:56 +00:00
Pierre Ayoub
5157ba6021 Update docs/plugins/smartplaylist.rst
Styling by Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-13 19:17:56 +00:00
Pierre Ayoub
1393cd0ae7 fix(test_smartplaylist.py): mypy type error as commit 002a051d0 2026-03-13 19:17:56 +00:00
Pierre Ayoub
7dfed4429f chore(docs): Lint double backquotes 2026-03-13 19:17:56 +00:00
Pierre Ayoub
523e504f5e fix(test_smartplaylist): Adapt to changes from commit d01727019
The aforementioned commits introduced a nmuber of changes since I
implemented this test:
- The syntax `self.assertExists(m3u_filepath)` was an old and now invalid
  way of checking existence of a path using assertion, change to `assert
  m3u_filepath.exists()` which now use string instead of bytes
- Use of `Path()` and strings instead of `path.join` and bytes for
  handling directory path
2026-03-13 19:17:56 +00:00
Pierre Ayoub
b798465a55 chore(docs): Format with docstrfmt 2026-03-13 19:17:56 +00:00
Pierre Ayoub
f5a08712b1 chore(test): Lint test_smartplaylist.py 2026-03-13 19:17:56 +00:00
Pierre Ayoub
e80fa746af chore(test): Format test_smartplaylist.py 2026-03-13 19:17:56 +00:00
Pierre Ayoub
2dcd8f4066 fix(test): Linting issues in smartplaylist 2026-03-13 19:17:56 +00:00
Pierre Ayoub
760271c456 chore(test): format with poe 2026-03-13 19:17:56 +00:00
Pierre Ayoub
ad2529adb0 test(plugins): write dest_regen test for smartplaylist
Test functions inspired from `test_playlist_update_output_extm3u()` in
`test_smartplaylist.py`.
Test successfully passed using:
`poetry run pytest test/plugins/test_smartplaylist.py`
2026-03-13 19:17:56 +00:00
Pierre Ayoub
ca9990c8c2 [beetsplug/spl] Fix linting error 2026-03-13 19:17:56 +00:00
Pierre Ayoub
a7aac37f66 [docs/changelog] Add log for dest_regen option 2026-03-13 19:17:56 +00:00
Pierre Ayoub
d81c0518c8 [docs/spl] Add documentation for new dest_regen option 2026-03-13 19:17:56 +00:00
Pierre Ayoub
aae0260484 [beetsplug/spl] Add the dest_regen/dest-regen option 2026-03-13 19:17:56 +00:00
Šarūnas Nejus
2c6f239ac5
Use va_name config for all artist fields on VA releases (#6438)
Fixes #6316

When importing compilations/various artists albums, several fields used the
hardcoded string "Various Artists" instead of the user-configured `va_name`
setting:

- In the **musicbrainz plugin**, only `info.artist` was overridden with `va_name`
when a release was identified as VA. The `artist_sort`, `artists_sort`,
`artist_credit`, `artists_credit`, and `artists` fields were left with the raw 
MusicBrainz value ("Various Artists"), which then propagated to
 `albumartist_sort`, `albumartists_sort`, `albumartist_credit`,
 `albumartists_credit`, and `albumartists` on items.

- In the **beatport plugin**, the VA artist name was hardcoded to
  "Various Artists" instead of reading from config.

## Changes

- `beetsplug/musicbrainz.py`: When `info.va` is true, override all artist-related
  fields (`artist_sort`, `artists`, `artists_sort`, `artist_credit`,
  `artists_credit`) with `va_name`, not just `artist`.
- `beetsplug/beatport.py`: Replace hardcoded "Various Artists" with
  `config["va_name"].as_str()`.
- `docs/changelog.rst`: Add changelog entries for both fixes.
2026-03-13 19:09:17 +00:00
Carson Jones
be722b564e Use va_name config for all artist fields on VA releases
When importing compilations, albumartist_sort, albumartists_sort,
albumartist_credit, albumartists_credit, and albumartists were
hardcoded to "Various Artists" instead of using the user-configured
va_name setting. This also fixes the same issue in the beatport plugin.

Fixes #6316
2026-03-12 23:00:32 -04:00
Šarūnas Nejus
f203bc5241
Add extra_tags support to Discogs plugin (#6433)
Fixes #6412.

This is my first time submitting a PR for an open source project so
please point out any mistakes!

## Summary

- Add `discogs.extra_tags` configuration option to narrow Discogs search
queries using existing tag values.
- Map supported tags (`barcode`, `catalognum`, `country`, `label`,
`media`, `year`) to corresponding Discogs search parameters.
- Update Discogs plugin documentation and tests to cover the new
behavior.

## Details

The Discogs plugin now mirrors `musicbrainz.extra_tags` by allowing
users to specify additional tags that should be used when building
Discogs search filters.

- New config option: `discogs.extra_tags` (default: `[]`).
- Supported tags and their Discogs search parameters:
  - `barcode` → `barcode`
  - `catalognum` → `catno` (whitespace removed)
  - `country` → `country`
  - `label` → `label`
  - `media` → `format`
  - `year` → `year`
- Tags `alias` and `tracks` are recognized but intentionally ignored for
Discogs, since the Discogs API does not provide direct equivalents for
these MusicBrainz-specific fields.

When `extra_tags` are configured, the plugin uses `beets.util.plurality`
over the items in the import session to select the most common value for
each configured tag and adds the corresponding Discogs filter.

## Testing

- Added unit tests in `test/plugins/test_discogs.py` to verify:
- Default search filters remain unchanged when `extra_tags` is not set.
- `discogs.extra_tags: [label, catalognum]` results in `label` and
`catno` filters populated from library items (with catalog number
whitespace stripped).
- Ran:
  - `pytest test/plugins/test_discogs.py`
  - `pytest test/plugins/test_musicbrainz.py`
2026-03-13 00:31:03 +00:00
john doe
1c3b58c7d8 Update discogs.rst
Formatting fix
2026-03-13 00:25:39 +00:00
Jack
c0c84cebeb Format discogs plugin after review changes 2026-03-13 00:25:39 +00:00
Jack
ce451d5f34 Remove alias/tracks special case handling for new Discogs extra_tags function 2026-03-13 00:25:39 +00:00
john doe
acda1755c0 Update test/plugins/test_discogs.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-03-13 00:25:39 +00:00
Jack
76e862e691 Fix formatting: run ruff format on discogs plugin 2026-03-13 00:25:39 +00:00
Jack
59b636ac8c CI: install discogs extra so discogs tests run; remove skip logic 2026-03-13 00:25:39 +00:00
Jack
da32d64599 Fix module-level skip: use pytest.skip(allow_module_level=True) for discogs 2026-03-13 00:25:39 +00:00
Jack
04e5d0083d CI: skip discogs tests when discogs_client missing, guard empty items, reset config in test 2026-03-13 00:25:39 +00:00
jdoe29103
99eec9ec25 Update changelog 2026-03-13 00:25:39 +00:00
jdoe29103
809744e8f4 Fix test class structure: move DGSearchQueryTest after DGAlbumInfoTest 2026-03-13 00:25:39 +00:00
jdoe29103
0d8d3bfadf Add discogs.extra_tags and updated documentation 2026-03-13 00:25:39 +00:00
Šarūnas Nejus
6af84bcfb6
Ensure changelog entries are under Unreleased section (#6432)
## Enforce Changelog Entries Under 'Unreleased' Section

I've had enough checking this manually 😆. Adds a CI lint step
that prevents contributors from accidentally adding changelog entries
under an already-released version header in `docs/changelog.rst`.

### How it works

The check runs `git diff --word-diff=plain -U1000` against the base
branch, then pipes through `awk` to scan the diff for new list entries
(`{+- ...`) that appear after any versioned release header (e.g. `1.2.3
(`). If such an entry is found, the step fails with a human-readable
error pointing to the offending line which GitHub should show in the
diff view.

* `--word-diff=plain` is required to match _truly new_ changelog entries
instead of some formatting adjustments in the middle of the line.
* `-U1000` should ensure that we grab the first 1000 lines in the
changelog to reliably match the headers.
2026-03-11 06:55:16 +00:00
Šarūnas Nejus
f969084a2a
Ensure changelog entries are under Unreleased section 2026-03-10 16:48:33 +00:00
Šarūnas Nejus
c89b94a467
docs(installation): Remove MacOS section from installation guide (#6427)
Fixes #5993

The section is outdated. MacOS (or HomeBrew's python package?) by
default disallows using `pip3 install` even with `--user` option.

I'd actually propose to add more information around how to use `pipx` in
this installation guide to install beets and its plugins (both provided
as extras and third-party), maybe even make it the main focus of this
page as a recommended way regardless of which OS and Linux distro is
used. I can create a separate issue/discussion to discuss and align on
this if it's fine by you.
2026-03-10 14:52:49 +00:00
Andrey M.
079b0276b8
Update docs/changelog.rst
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-03-10 12:16:59 +00:00
Andrejs Mivreniks
c019d790c5 docs(installation): Remove redundant MacOS section from installation guide 2026-03-10 13:06:16 +02:00
Šarūnas Nejus
3a8e65c515
Use aliases for tracks, releases and release groups (#6231)
> This PR add support for aliases to releases, release-groups and
recordings.

> This PR is a must have (IMO at least) for people that listen to
Japanese, Chinese and other songs that has other symbols for letters.
With this, not only the artist name will use the alias if available, but
now the album and track name will also use aliases.

Follow up from https://github.com/beetbox/beets/pull/5277 based on the
recent new mechanism to query musicbrainz
(https://github.com/beetbox/beets/pull/6052).

## API examples

- Aliases for releases, recordings and release groups:
http://musicbrainz.org/ws/2/release/59211ea4-ffd2-4ad9-9a4e-941d3148024a?inc=recordings+aliases+release-groups&fmt=json
- Aliases for recordings:
https://musicbrainz.org/ws/2/recording/b9ad642e-b012-41c7-b72a-42cf4911f9ff?inc=aliases&fmt=json
2026-03-10 08:24:08 +00:00
Eric Masseran
548bd9bab6 Merge remote-tracking branch 'official/master' into use-aliases-for-track-album
* official/master: (54 commits)
  Require data_source in album_for_id and track_for_id functions
  Invoke album_matched hook from AlbumMatch.__post_init__
  Refactor match_by_id
  Take data source into account when deciding duplicate candidates
  Return album candidates from multiple sources when matching by IDs
  Add a test to reproduce the issue
  Move assignment tests to test/autotag/test_match.py
  Pulled latest changelog and added my entry to 'Unreleased > Bug fixes' section.
  Moved changelog note to top, under Unreleased.
  This PR improves the regex detection used for the drive_sep_replace default.
  This PR improves the regex detection used for the drive_sep_replace default.
  refactor: Use deprecate_for_user for beatport/bpsync deprecation warnings
  Fix docs: use single-line deprecated directive compatible with docstrfmt
  Fix docs formatting for beatport and bpsync rst files
  Deprecate beatport and bpsync plugins
  Update changelog.rst
  try to fix fish plugin
  Make get_search_query_with_filters abstract
  Document new methods
  Document shared metadata search plugin workflow
  ...
2026-03-10 08:52:31 +01:00
Eric Masseran
efd2b090b5 Copilot feedback 2026-03-10 08:52:08 +01:00
Šarūnas Nejus
abd77b35ac
Return candidates from all data sources on id search (#6184)
Closes #6178 (multiple metadata source results per ID) and #6181
(duplicate/overwrite of candidates).

- (#6178) Replace `album_for_id` / `track_for_id` with `albums_for_ids`
/ `tracks_for_ids` in `metadata_plugins` that yield candidates from all
metadata sources
- (#6181) Use `Info.identifier` (`(data_source, id)`) as candidate keys
to avoid cross-source ID collisions.
- Add tests (`test/autotag/test_match.py`) for assignment logic and
multi-source ID matching
- Simplify `match_by_id`
- Dedupe `album_matched` event emission by moving it to
`AlbumMatch.__post_init__` (and convert `AlbumMatch` / `TrackMatch` to
dataclasses)
2026-03-10 01:02:56 +00:00
Šarūnas Nejus
35361a63b6
Require data_source in album_for_id and track_for_id functions
These functions now accept both an ID and data_source parameter,
enabling plugins like mbsync and missing to retrieve metadata from the
correct source.

Update mbsync and missing plugins to use the restored functions with
explicit data_source parameters. Add data_source validation to prevent
lookups when the source is not specified.

Add get_metadata_source helper function to retrieve plugins by their
data_source name, cached for performance.
2026-03-10 00:56:35 +00:00
Šarūnas Nejus
fcfa8ddfac
Invoke album_matched hook from AlbumMatch.__post_init__ 2026-03-10 00:56:35 +00:00
Šarūnas Nejus
de25d7623b
Refactor match_by_id 2026-03-10 00:56:14 +00:00
Šarūnas Nejus
85aa1d7a9c
Take data source into account when deciding duplicate candidates 2026-03-10 00:56:13 +00:00
Šarūnas Nejus
094d5bfb9d
Return album candidates from multiple sources when matching by IDs 2026-03-10 00:55:38 +00:00
Šarūnas Nejus
a64bde86bf
Add a test to reproduce the issue 2026-03-10 00:55:38 +00:00
Šarūnas Nejus
2f28fbc6e7
Move assignment tests to test/autotag/test_match.py 2026-03-10 00:55:38 +00:00
Šarūnas Nejus
44dc3cd4e9
Improve regex detection for the drive_sep_replace default (#6417)
I imported an album where a track had the name `1:00 AM - Clear` and
another track named `12:00 AM - Clear` (just two examples).

See: [Animal Crossing: New Horizons
OST](https://musicbrainz.org/release/263f7ed3-60c2-4251-ac7d-6da3f8691256)

After import, the former was renamed `1_00 AM - Clear`, and the latter
`12;00 AM - Clear`. Notice the inconsistency of how the `:` was
replaced.

I did not make use of the (hidden) `drive_sep_replace` setting. These
were my `replace` settings:
```
replace:                        # prevent file name incompatibiliy
  '[\s]'                        : ' '   # standardize whitespace
  '["`‘’“”]'                    : "'"   # standardize quotes
  '[\u002D\u2010-\u2015\u2E3A]' : '-'   # standardize dashes
  '[\u2E3B\uFE58\uFE63\uFF0D]'  : '-'   # standardize dashes
  '[\xAD]'                      : '-'   # standardize dashes
  '[\\\|\/]'                    : ' '   # slashes, pipe > space
  '[:]'                         : ';'   # colon > semicolon
  '[<>]'                        : '-'   # chevrons > dashes
  '[\?\*]'                      : ''    # remove restricted characters
  '[\x00-\x1F\x7F]'             : ''    # remove basic control characters
  '[\x80-\x9F]'                 : ''    # remove extra control characters
  '^\.'                         : ''    # remove leading period
  '\.$'                         : ''    # remove trailing period
  '^\s+'                        : ''    # remove leading space
  '\s+$'                        : ''    # remove trailing space
```

I found the issue to be too generic regex for drive separator detection.
I'm on macOS, so this is irrelevant to me anyway (and I got around it by
adding `drive_sep_replace: ';'` to my settings), but regardless, I think
this could be improved.

This PR improves the regex to detect drive separators. Instead of merely
looking for any first character followed by a colon (`^\w:`), we look
for a letter, followed by a colon, followed by a backslash instead
(`^[a-zA-Z]:\\`).

The regex logic is solid, but I am not able to test this on a real
Windows environment.

~Still have to add an entry to the changelog, will do so soon.~

# Update

Initially this commit failed the
`MoveTest.test_move_file_with_colon_alt_separator` test because it
checks the logic using a `C:DOS` path. So I had to make the logic less
restrictive again, not checking for a backslash (`^[a-zA-Z]:`). I would
argue the test itself should be amended (test with `C:\DOS` instead),
but that's not up to me.

As a result, my case of "1:00 AM" being replaced incorrectly is still
resolved, but other hypothetical cases like "a:b" would still not be
covered due to an arguably incorrect test limiting a more precise regex.
2026-03-10 00:16:56 +00:00
spaceage64
fbe9495bdd Pulled latest changelog and added my entry to 'Unreleased > Bug fixes' section. 2026-03-10 00:25:02 +01:00
spaceage64
b8823dddde Merge remote-tracking branch 'origin/drive-separator-fix' 2026-03-10 00:14:25 +01:00
spaceage64
031c2a8aeb Moved changelog note to top, under Unreleased. 2026-03-10 00:09:32 +01:00