Commit graph

475 commits

Author SHA1 Message Date
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
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
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
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
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
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
9b63985989
Migrate MusicBrainz to shared search hooks
Move MusicBrainzPlugin to SearchApiMetadataSourcePlugin hooks.

Keep entity mapping and criteria in provider-specific hooks.

Update typing and tests for the candidate search path.
2026-03-08 09:00:14 +00:00
Šarūnas Nejus
b683cb6540
Extend requires_import marker with force_ci option
Add force_ci kwarg to requires_import pytest marker to allow tests
to run unconditionally in CI (GitHub Actions), even if the module
is not detected locally. Refactor autobpm test to use this instead
of manual env-checking at module level.
2026-03-08 08:19:55 +00:00
Šarūnas Nejus
a7325e7483
Skip langdetect-dependent tests when package is not installed
Add a `requires_import` pytest marker and `is_importable` fixture to
conditionally skip or adjust assertions based on whether optional
dependencies are available. Apply this to `langdetect`-dependent
language detection tests in lyrics and migration test suites.
2026-03-08 08:19:55 +00:00
Šarūnas Nejus
05a822dd5c
Move Lyrics class under beets.util as it is used by migrations 2026-03-06 10:57:08 +00:00
Šarūnas Nejus
7d30efa82c
Migrate lyrics metadata to flex fields on library open
- Add `LyricsMetadataInFlexFieldsMigration` to extract legacy source
  URLs and language metadata from lyrics text into flex attributes
- Add `Lyrics.from_legacy_text` to parse legacy lyrics format
- Move `with_row_factory` context manager up to base `Migration` class
- Rename `migrate_table` to `migrate_model` and pass model class
  instead of table name string. This is so that the migration can access
  both `_table` and `_flex_table` attributes.
- Make `langdetect` import optional in `Lyrics.__post_init__`: users may
  not have have the dependency installed, and we do not want the
  migration to fail because of that.
- Move `BACKEND_BY_NAME` to module level for use outside plugin class
2026-03-06 10:57:08 +00:00
Šarūnas Nejus
7df14e1877
Fix timestamp format in translation/synced lyrics test 2026-03-06 10:57:08 +00:00
Šarūnas Nejus
38708ae592
Refactor lyrics handling to use structured Lyrics object
* Introduce a `Lyrics` dataclass to carry text, source URL, and language
  metadata through fetch, translation, and storage paths.
* Return `Lyrics` from backends and plugin lookup methods instead of raw
  tuples/strings.
* Store backend name in `lyrics_source` derived from fetched URL root
  domain.
* Simplify translator flow to operate on `Lyrics`, reuse line splitting,
  append translations in-place, and record translation language
  metadata.
2026-03-06 10:57:08 +00:00
Šarūnas Nejus
835115a6f7
Fix genius end to end lyrics test 2026-03-06 10:57:08 +00:00
Šarūnas Nejus
c239275193
Do not split orig/trans if they are not different 2026-03-05 15:36:59 +00:00
Šarūnas Nejus
cd95c15a0b
Add a test to show duplicate translations we have right now 2026-03-05 15:36:59 +00:00
Šarūnas Nejus
82bfc03494
Preserve synced lyrics when fetched result is plain text
When lyrics.synced is enabled, avoid replacing existing synced lyrics with
newly fetched unsynced lyrics, even with force enabled.

Allow replacement when the new lyrics are also synced, or when synced mode
is disabled.
2026-03-05 15:36:59 +00:00
Šarūnas Nejus
24ca6abcfe
lyrics: validate synced lyrics duration 2026-03-05 15:34:32 +00:00
Eric Masseran
7d81f58bd3 Fix type dep 2026-03-04 22:54:33 +01:00
Eric Masseran
1fe5bad4b2 Merge remote-tracking branch 'official/master' into use-aliases-for-track-album
* official/master: (180 commits)
  feat(lastgenre): cleanup_existing
  convert: generate playlist entries from effective output paths
  Fix lint issues
  Move changelog note under Unreleased section
  Enable duplicate detection for as-is imports
  Force slow queries for FuzzyPlugin
  Add tests
  Add changelog note
  Match substrings fuzzily
  Fix lint
  Move test_autotag tests under test/autotag
  Keep missing multi-value fields as None instead of empty list
  Show that album genres are not applied to tracks
  autotag: refactor autotag tests to use single comprehensive test
  fix(lastgenre): Reset plugin config in fixtured tests
  fix(fetchart): prevent deletion of configured fallback cover art
  Move changelog note under unreleased section
  Update changelog note
  fix: ftintitle can handle a list of ampersanded artists
  Fix symlink tests for macOS
  ...
2026-03-04 22:21:03 +01:00
Arne Beer
13fe82f394 feat(lastgenre): cleanup_existing
Introduce a new lastgenre `cleanup_existing` flag.

It handles the case where canonicalization is desired on existing tags.
The new logic triggers if:
- `force`: False
- `cleanup_existing: True

Depending on whether `whitelist: True` or `canonical: True`, the genres
are then canonicalized and/or whitelisting is applied
2026-03-04 21:01:37 +01:00
Šarūnas Nejus
a5a9775930
convert: generate playlist entries from effective output paths
Build playlist paths using the selected format (`--format`/config), and only
replace extensions when the destination file is actually transcoded.

Precompute playlist entries before conversion runs so `--keep-new` does not
pick up mutated item paths and produce mismatched extensions.

Add/expand convert CLI tests to cover:
- config format playlist extension
- `--format` override playlist extension
- no-transcode (`no_convert`) playlist extension
- `--keep-new` destination playlist path behavior
2026-03-04 15:38:33 +00:00
Šarūnas Nejus
bc9213a4ed
Fix lint issues 2026-03-04 15:28:30 +00:00
Šarūnas Nejus
c46f99a82b
Merge branch 'master' into pr-fix-convert-ext 2026-03-04 15:22:40 +00:00
Šarūnas Nejus
b82a8eaab7
Add tests 2026-03-03 13:53:12 +00:00
Šarūnas Nejus
1838482c7a
Keep missing multi-value fields as None instead of empty list 2026-03-03 07:48:51 +00:00
Arne Beer
723b4bbfe9
fix(lastgenre): Reset plugin config in fixtured tests 2026-03-03 00:43:05 +01:00
Danny Trunk
974d917df4 fix(fetchart): prevent deletion of configured fallback cover art
When `import.delete` or `import.move` is enabled, the `assign_art` method calls `task.prune(candidate.path)` unconditionally.
This incorrectly deletes the configured `fetchart.fallback` file.
Add explicit check to skip pruning when the candidate path matches the configured fallback.
2026-03-02 18:10:19 +01:00
Šarūnas Nejus
91fae7c879
Merge branch 'master' into zero-total-discs 2026-03-02 16:37:43 +00:00
Fredrik Möllerstrand
bfd95f47d0 fix: ftintitle can handle a list of ampersanded artists
This was inspired by real life events:
https://musicbrainz.org/release/7c4d7a15-6b30-4bef-8b20-af200186fbdb
by the artist Danny L Harle has a a track with a featuring list
that contains "Danny L Harle, Oklou & MNEK".
2026-03-02 14:53:23 +00:00
Šarūnas Nejus
10d13992e6
Dedupe genres parsing in beatport 2026-02-27 18:36:04 +00:00
Šarūnas Nejus
2c63fe77ce
Remove test case indices from test_lastgenre.py 2026-02-27 18:36:04 +00:00
Šarūnas Nejus
a8d53f78de
Fix the rest of the tests 2026-02-27 18:34:26 +00:00
dunkla
9003107ee7
Remove noisy comments from beatport tests (ref https://github.com/beetbox/beets/pull/6169#issuecomment-3716893013) 2026-02-27 18:24:54 +00:00
dunkla
67ce53d2c6
Remove conditional logic from lastgenre tests (ref https://github.com/beetbox/beets/pull/6169#issuecomment-3716893013) 2026-02-27 18:24:54 +00:00
dunkla
36a30b3c65
Implement automatic database-level genre migration
- Add Library._make_table() override to automatically migrate genres when database schema is updated
- Migration splits comma/semicolon/slash-separated genre strings into genres list
- Writes changes to both database and media files with progress reporting
- Remove lazy migration from correct_list_fields() - now handled at database level
- Remove migration-specific tests (migration is now automatic, not lazy)
- Update changelog to reflect automatic migration behavior

Related PR review comment changes:
- Replace _is_valid with _filter_valid method in lastgenre plugin
- Use unique_list and remove genre field from Beatport plugin
- Simplify LastGenre tests - remove separator logic
- Document separator deprecation in lastgenre plugin
- Add deprecation warning for genre parameter in Info.__init__()
2026-02-27 18:24:54 +00:00
dunkla
c55b6d103c
shorte test description in test/plugins/test_lastgenre.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-02-23 05:11:37 +00:00
dunkla
1c0ebcf348
remove noisy comment from test/plugins/test_beatport.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-02-23 05:11:36 +00:00
Johann Fot
70bf57baf6
Add native support for multiple genres per album/track
Simplify multi-genre implementation based on maintainer feedback (PR #6169).

Changes:
- Remove multi_value_genres and genre_separator config options
- Replace complex sync_genre_fields() with ensure_first_value('genre', 'genres')
- Update all plugins (Beatport, MusicBrainz, LastGenre) to always write genres as lists
- Add automatic migration for comma/semicolon/slash-separated genre strings
- Add 'beet migrate genres' command for explicit batch migration with --pretend flag
- Update all tests to reflect simplified approach (44 tests passing)
- Update documentation

Implementation aligns with maintainer vision of always using multi-value genres
internally with automatic backward-compatible sync to the genre field via
ensure_first_value(), eliminating configuration complexity.

Migration strategy avoids problems from #5540:
- Automatic lazy migration on item access (no reimport/mbsync needed)
- Optional batch migration command for user control
- No endless rewrite loops due to proper field synchronization
2026-02-23 05:11:36 +00:00
Šarūnas Nejus
743a59be3b
Fix replaygain tests 2026-02-21 16:15:46 +00:00
Šarūnas Nejus
3b670cdf18
In test I/O utility, restore the old stdin/stdout instead of the "true" I/O streams (#5049)
I got a little bit nerdsniped by the problems observed in #5027. In
short, my high-level diagnosis in
https://github.com/beetbox/beets/pull/5027#issuecomment-1857953929 seems
to have been correct: other tests were suppressing the legitimate
failure of a flaky test.

I found the problem by running other tests before the problem test, like
this:

```
$ pytest -k 'test_nonexistant_db or test_delete_removes_item' test/test_ui.py
```

When running `test_nonexistant_db` alone, it fails. When running it like
this with another test that goes first, it passes. That's the problem.

However, `test_delete_removes_item` is just one example that works to
make this problem happen. It appeared that _any_ test in a class that
used our `_common.TestCase` base class had this power. I tracked down
the issue to our `DummyIO` utility, which was having an unintentional
effect even when it was never actually used.

Here's the solution. Instead of restoring `sys.stdin` to
`sys.__stdin__`, we now restore it to whatever it was before we
installed out dummy I/O hooks. This is relevant in pytest, for example,
which installs its *own* `sys.stdin`, which we were then clobbering.
This was leading to the suppression of test failures observed in #5021
and addressed in #5027.

The CI will fail for this PR because it now (correctly) exposes a
failing test. Hopefully by combining this with the fixes in the works in
#5027, we'll be back to a passing test suite. 😃 @Phil305, could
you perhaps help validate that hypothesis?

Edit: @snejus:

I've now consolidated test I/O handling by removing the legacy
`control_stdin`/`capture_stdout` context managers and the custom
`DummyOut` stream, replacing them with a pytest-driven `io` fixture
that:
- provides controllable `stdin` via a lightweight `DummyIn`
- captures `stdout` via `capteesys`
- attaches a `DummyIO` helper to test classes as `self.io`
2026-02-14 18:03:34 +00:00
Fredrik Möllerstrand
cd9f86ae6d
feat: zero plugin zeroes disctotal if single disc
When omit_single_disc is set, disctotal is now also zeroed alongside disc.
Previously, only the disc tag was zeroed.
2026-02-14 18:32:04 +01:00
w4grfw
fe833d8377 test added 2026-02-11 18:23:55 +01:00
Šarūnas Nejus
d613981efe
Replace capture_output with io.getoutput 2026-02-10 00:53:20 +00:00