Commit graph

13705 commits

Author SHA1 Message Date
Šarūnas Nejus
f1509a40f9
Fix fish plugin (#6428)
Fixes #6340
2026-03-08 13:24:01 +00:00
Konstantin
d2705fef4e Update changelog.rst 2026-03-08 14:16:49 +01:00
Konstantin
b64bda86ff try to fix fish plugin 2026-03-08 13:57:42 +01:00
Šarūnas Nejus
3c48b0c6e6
Centralise common autotagger search functionality (#5982)
Add a unified search abstraction across metadata source plugins.

Summary:
- Introduces `SearchApiMetadataSourcePlugin` with `SearchParams`,
`get_search_query_with_filters`, and `get_search_response` hooks to
standardize album/track searches.
- Replaces ad-hoc `_search_api` and query construction logic in Deezer,
Spotify, MusicBrainz, and Discogs plugins with the new shared
implementation.
- Refactors Discogs and MusicBrainz plugins to use the new abstraction
and move provider-specific criteria/query construction into hook
methods.
- Centralizes error handling and logging in the shared search flow;
Spotify now retries authentication once on `401`, and failures cleanly
fall back to empty results at the shared layer.
2026-03-08 09:17:48 +00:00
Šarūnas Nejus
46aa0a4ba5
Make get_search_query_with_filters abstract 2026-03-08 09:06:00 +00:00
Šarūnas Nejus
0670611d7a
Document new methods 2026-03-08 09:00:14 +00:00
Šarūnas Nejus
4cccb70921
Document shared metadata search plugin workflow
Add developer guidance for SearchApiMetadataSourcePlugin and its hooks.

Clarify migration advice for API-backed metadata source plugins.
2026-03-08 09:00:14 +00:00
Šarūnas Nejus
9855d46901
Bound Spotify auth retry during search
Replace recursive 401 handling with a single retry loop.

This prevents unbounded recursion when authentication keeps failing.
2026-03-08 09:00:14 +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
27bb34411c
Refactor shared search API flow
Move search orchestration into SearchApiMetadataSourcePlugin.

Migrate Deezer, Spotify, and Discogs to provider hooks.

Keep query handling, logging, and limits centralized.
2026-03-08 09:00:14 +00:00
Šarūnas Nejus
9f6a4b4147
Clarify %if template behavior (#6399)
Fixes #4991.

I don't know if there's a single, good workaround for this, so I opted
to not add an example solution.
2026-03-08 08:57:55 +00:00
Emil Hammarberg
1f2beb6d02 Format 2026-03-08 08:51:42 +00:00
Emil Hammarberg
763dc7ccb1 Fix empty literal error 2026-03-08 08:51:42 +00:00
Emil Hammarberg
8a57472224 Reword 2026-03-08 08:51:42 +00:00
Emil Hammarberg
654b76190c Clarify %if template behavior
Closes #4991
2026-03-08 08:51:42 +00:00
Šarūnas Nejus
6ef2a8cb0d
docs(CONTRIBUTING): Fix typo (#6423) 2026-03-08 08:50:12 +00:00
Pierre Ayoub
af42992cd9 docs(CONTRIBUTING): Fix typo 2026-03-08 08:45:09 +00:00
snejus
13665a5a55 Increment version to 2.7.1 2026-03-08 08:30:00 +00:00
Šarūnas Nejus
1acbdbd67c
Skip langdetect-dependent tests when package is not installed (#6422)
## Skip `langdetect`-dependent tests when package is unavailable

Fixes #6421.

### What changed

- Adds a `requires_import(module)` pytest marker to `conftest.py` that
skips tests when an optional dependency is not installed
- Adds an `is_importable` fixture for conditional assertions within a
test body
- Marks `TestTranslation` and other `langdetect`-dependent
tests/assertions with the new marker or fixture
- Extends `requires_import` with a `force_ci=True` kwarg — tests marked
this way are **never** skipped in `beetbox/beets` CI, ensuring coverage
where all deps are installed
- Moves marker declarations from `setup.cfg` into `pytest_configure` in
`conftest.py`
2026-03-08 08:26:58 +00:00
Šarūnas Nejus
bd2a448ca6
Document custom marks in CONTRIBUTING.rst 2026-03-08 08:19:55 +00:00
Šarūnas Nejus
a8b34d2976
Pin docstrfmt>=2.0.2 and add --preserve-adornments flag 2026-03-08 08:19:55 +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
13ad15b83e
Move pytest marker definitions to conftest.py
Replace static marker declarations in setup.cfg with dynamic
registration via pytest_configure hook in conftest.py.
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
2678124b88
Upgrade all dependencies (#6420)
See

<img width="1029" height="1235" alt="image"
src="https://github.com/user-attachments/assets/979fbc1b-385c-4ba5-b48a-d7658d6e7a0d"
/>
2026-03-07 21:56:43 +00:00
Šarūnas Nejus
02145a3ac0
Upgrade all dependencies 2026-03-07 21:33:24 +00:00
snejus
156870419e Increment version to 2.7.0 2026-03-07 21:16:10 +00:00
Šarūnas Nejus
c996a42f6d
Add docs fixes to git blame ignore revs 2026-03-07 19:43:52 +00:00
Šarūnas Nejus
cff5434cf7
Fix docs links (#6418)
## Documentation Link Maintenance

This PR audits and fixes all hyperlinks across the beets documentation.
No functional code changes — purely doc hygiene.

### What changed

- **HTTP → HTTPS**: Upgrades all `http://` links to `https://` where
supported.
- **Broken/outdated URLs**: Replaces dead or redirected links with their
current canonical targets. Key examples:
  - `codecov.io` → `app.codecov.io`
- Python docs from `docs.python.org/library/` →
`docs.python.org/3/library/`
  - PEP links from `python.org/dev/peps/` → `peps.python.org/`
  - SourceForge project links updated to current project page URLs
- Spotify, Plex, Sonos, Discogs, IPFS, and other service URLs updated to
their current domains/paths
- Archived URLs wrapped in `web.archive.org` where the original is gone
(e.g. `echonest`, `albumart.org`, `phash`)
- **`docs/conf.py`**: Adds link-check exclusions for domains known to
block automated requests (SourceForge, fanart.tv, Imgur, Discogs
settings).

### Impact

No user-facing behaviour changes. Fixes broken links that would
frustrate contributors and users reading the docs, and unblocks the
Sphinx `linkcheck` builder from false positives.
2026-03-07 19:40:59 +00:00
Šarūnas Nejus
b3f5585849
Add a changelog note 2026-03-06 11:29:05 +00:00
Šarūnas Nejus
192217da5d
Format docs 2026-03-06 11:28:29 +00:00
Šarūnas Nejus
ae3a2e5729
Fix redirect URLs 2026-03-06 11:28:29 +00:00
Šarūnas Nejus
441c838387
Fix broken URLs 2026-03-06 11:28:29 +00:00
Šarūnas Nejus
3d0d032987
Replace http URLs with https 2026-03-06 11:28:29 +00:00
Šarūnas Nejus
a6ac5eff5b
Add lyrics_url, lyrics_backend flex attrs, improve lrclib reliability (#6393)
Fixes: #6370

This PR completes the lyrics pipeline refactor around a structured
`Lyrics` value object and aligns storage, migration, and docs with that
model.

At a high level, lyrics handling is now end-to-end structured instead of
ad-hoc string/tuple flows: fetchers return `Lyrics`, translation
operates on `Lyrics`, and persistence writes both canonical text and
structured metadata.

High-level impact:

- Backends now return `Lyrics` instead of `(text, url)` tuples.
- Lyrics source metadata is no longer embedded in `item.lyrics` as a
`Source: ...` suffix.
- Lyrics metadata is stored in flexible fields:
`lyrics_backend`, `lyrics_url`, `lyrics_language`,
`lyrics_translation_language`.
- Existing libraries are automatically migrated on first run by a
one-time data migration that:
normalizes legacy mixed-content lyrics text and moves auxiliary metadata
into flex fields.
- Sync safety is improved:
with `synced` enabled, existing synced lyrics are not replaced by newly
fetched plain lyrics, even with `force`.
- LRCLib synced lyrics validation is stricter:
synced results are accepted only when the final synced timestamp is
consistent with track duration.

Docs and tests:

- Lyrics plugin docs now describe the new flexible metadata fields and
synced replacement behavior.
- Developer docs now document migration lifecycle, class-name-based
migration identity, and migration use cases.
- Changelog updated for all user-visible behavior changes.
- Tests were expanded/updated for migration behavior, backend return
types, translation behavior, synced-lyrics safety, and LRCLib duration
validation.
2026-03-06 11:27:53 +00:00
Šarūnas Nejus
ace6a99d07
Document methods changed/introduced 2026-03-06 10:57:08 +00:00
Šarūnas Nejus
e8605747d6
Document migrations 2026-03-06 10:57:08 +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
50a564e66d
Set constant langdetection seed for stable results 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
J0J0 Todos
edbf737638
Lastgenre: Cleanup existing genres (#6317)
## Description

Fixes #6305 

Implements a new `cleanup_existing` config flag.
2026-03-04 21:06:38 +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
c1fa0a653d
Fix extension issue in convert plugin when exporting a playlist (#5203)
Fix extension substitution inside path of the exported playlist.

Before this, the exported playlist contained relative paths pointing to
the
converted files BUT the extension were not substituted comparing to
before and
the after the conversion. Therefore, running the playlist will fail for
files
which have been converted and where extension have changed.

Example:
1. Convert `/path/to/library/artist.flac` to
`/path/to/converted/artist.mp3` using the `-m playlist.m3u` command-line
flag.
2. Open the generated playlist, and find the incorrect path
`/path/to/converted/artist.flac` inside.
2026-03-04 17:04:27 +00:00