Commit Graph

14035 Commits

Author SHA1 Message Date
Sebastian Mohr
7df2789cfd Fixed metadatasource tests. 2026-04-11 11:42:30 +02:00
Sebastian Mohr
8637f138df Fixed some typing issues with rebase 2026-04-11 11:20:31 +02:00
Sebastian Mohr
c029f1f363 test_logging:LoggingLevelTest now uses pytest PluginMixin 2026-04-11 11:05:03 +02:00
Sebastian Mohr
3f9ec711e1 Set the class module during plugin registration to not trigger the plugin
load dedup.
2026-04-11 11:05:03 +02:00
Sebastian Mohr
25255816fa Fixed issue with mpdstats test and removed unregister_all function
as it introduces issues.
2026-04-11 11:05:03 +02:00
Sebastian Mohr
d62bd277fc Renamed PluginTestCasePytest to PluginTest 2026-04-11 11:04:49 +02:00
Sebastian Mohr
04b2266ad7 Refactored most plugin tests to use pytest instead of unitest.
Also simplified test wherever possible.
2026-04-11 11:04:38 +02:00
J0J0 Todos
0f2bb215ec Slightly simplify lastgenre client (#6495)
## Consolidate Last.fm genre fetching into a single `fetch` method

This PR simplifies the `lastgenre` client API by replacing three
separate fetch methods (`fetch_track_genre`, `fetch_album_genre`,
`fetch_artist_genre`) with a single unified `fetch(kind, obj)` method.

### What changed

**`client.py`**:
- Introduces a class-level `FETCH_METHODS` registry (`ClassVar` dict)
mapping fetch "kinds" (`"track"`, `"album"`, `"artist"`,
`"album_artist"`) to a `(pylast_method, arg_extractor)` tuple.
- Replaces the three `fetch_*` methods with a single `fetch(kind, obj)`
that dispatches via this registry.
- Removes a private `_tags_for` wrapper — its logic is inlined into the
now-public `fetch_genres`.
- Drops the workaround for a `pylast.Album.get_top_tags()` inconsistency
[fixed in 2014](https://github.com/pylast/pylast/issues/86).

**`__init__.py`**:
- All call sites updated to use `client.fetch(kind, obj)` — the client
now owns field extraction (e.g. `obj.artist`, `obj.album`), removing
that concern from the plugin layer.

**`test_lastgenre.py`**:
- Test mocking simplified: a single `monkeypatch` on
`LastFmClient.fetch` replaces three separate method patches.

### Impact

- **Reduced surface area**: one method to mock, test, and reason about
instead of three.
- **Field extraction centralised**: callers no longer need to know which
fields to pass per entity type.
- **No behaviour change** — pure refactor.
2026-04-11 02:55:31 +02:00
J0J0 Todos
e0b757ceb3 Remove unused import in lastgenre tests 2026-04-11 02:39:50 +02:00
Šarūnas Nejus
2913accb73 Add JOJ0 as a lastgenre codeowner 2026-04-11 02:25:02 +02:00
Šarūnas Nejus
1f490f4611 Remove special handling for pylast.Album.get_top_tags
This issue has been resolved in 2014.
2026-04-11 02:25:02 +02:00
Šarūnas Nejus
19353999ae Simplify fetch_genre 2026-04-11 02:25:02 +02:00
Šarūnas Nejus
f66cc3fb57 lastgenre: use a single fetch method
Delegate the responsibility of getting relevant model fields to the
client by declaring the genre fetching spec on the class.
2026-04-11 02:25:02 +02:00
Šarūnas Nejus
72b1118ad0 Support multi-valued fields in rewrite and advancedrewrite plugins (#6518)
## Fix rewriting of multi-valued fields (`rewrite` / `advancedrewrite`
plugins)

**Bug:** Both `rewrite` and `advancedrewrite` plugins assumed all field
values are scalars, so list-type fields (e.g. `genres`) were not
rewritten correctly. Additionally, only the first matching rule was ever
applied to a field.

---

### What changed

**Core logic (`beetsplug/rewrite.py`):**

- Introduced a `rewrite_value` `singledispatch` function to handle both
`str` and `list[str]` values. For lists, each element is rewritten
individually.
- Extracted `apply_rewrite_rules` as a shared utility — now applies
**all** matching rules in config order (previously stopped at the first
match).

**`advancedrewrite` plugin:**

- Replaced its own inline rule-matching loop with a call to the shared
`apply_rewrite_rules`, fixing list field support there too.

**Behaviour change — rule application order:**

Previously, only the first matching rule was applied. Now, all rules run
in config order, allowing chained rewrites. For example:

```yaml
rewrite:
    artist .*hendrix.*: hendrix catalog
    artist .*catalog.*: Experience catalog
```

This now produces `"Experience catalog"` instead of `"hendrix catalog"`.
2026-04-10 18:39:41 +01:00
Šarūnas Nejus
2ff7887725 Apply all matching rules to a field 2026-04-10 14:54:57 +01:00
Šarūnas Nejus
c9064012a1 Show that only the first pattern applied currently 2026-04-10 11:52:15 +01:00
Šarūnas Nejus
e21f56fc60 Update docs 2026-04-10 10:43:25 +01:00
Šarūnas Nejus
c81cfebc7b Handle list fields in rewrite 2026-04-10 10:43:25 +01:00
Šarūnas Nejus
bfd20b2abc Add a failing test for list fields 2026-04-10 10:37:36 +01:00
Šarūnas Nejus
9b6b7d3cd6 Add tests for rewrite plugin 2026-04-10 09:34:55 +01:00
J0J0 Todos
fd586ef605 lastgenre: Genre ignorelist (#6449)
## Description

Adds a global and artist-specific genre ignorelist to lastgenre.
Ignorelist entries can use regex patterns or literal genre names and are
configurable per artist or globally. For config examples see submitted
docs and `_load_ignorelist()` docstring.

### Additional minor refactoring

- Fixed condition in "keep original fallback stage" to use config view
object directly via `.get()`.
- Deduplicate finding the correct artist/albumartist attribute with a
helper `_artist_for_helper`
2026-04-09 22:56:50 +02:00
J0J0 Todos
570d1d3bdc Changelog for #6449 genre ignorelist 2026-04-09 22:42:38 +02:00
J0J0 Todos
fc367165f9 lastgenre: Docs for genre ignorelist feature 2026-04-09 22:42:38 +02:00
J0J0 Todos
770682b4cf lastgenre: Dedup fallback handling in _get_genre
and fix fallback types
2026-04-09 22:41:39 +02:00
J0J0 Todos
4ac0aa8a6a lastgenre: Genre ignorelist based on artist
- Prevents wrong last.fm genres based on a per artist (or global) list of regex
  patterns that should be ignored.
  - Genre _ignoring_ happens in two places but mainly:
    - Right after fetching from last.fm
    - and in _resolve_genres (via filter_valid or directly).
  - As a fallback literal string matching can be used instead of
    supplying a regex pattern

New methods:
- `artist_for_filter` to find out which (album)artist attribute is the
  right one in a stage -> ignorelist is artist-based!
- `is_ignored` and `drop_ignored_genres`
- `load_ignorelist` uses confuse mechanisms to load patterns for each
  artist and provide them to the plugin as self.ignore_patterns
2026-04-09 21:53:26 +02:00
J0J0 Todos
43e13dcfd3 lastgenre: Tests for genre ignorelist feature 2026-04-09 21:50:29 +02:00
Šarūnas Nejus
2334b222aa Import MusicBrainz composer/lyricist/arranger ids (#5847)
Updates the MusicBrainz plugin to also import MBIDs for
composers/lyricists/arrangers, and adds them as multi-valued fields.

Closes #5698.
2026-04-09 16:37:04 +01:00
Maxr1998
39b8a8ec02 Address further review feedback 2026-04-09 17:18:25 +02:00
Maxr1998
633703832d Address further review feedback 2026-04-09 16:18:40 +02:00
Maxr1998
08af801c30 Address review feedback 2026-04-09 10:46:40 +02:00
Maxr1998
9bc865f6f3 Add changelog 2026-04-09 10:40:59 +02:00
Maxr1998
ee1a1632a0 Fix tests 2026-04-09 10:40:59 +02:00
Maxr1998
4ac2c90aa6 Add remixer ids and merge recording artist relations query 2026-04-09 10:40:59 +02:00
Maxr1998
90b8c2c4cf Import MusicBrainz composer/lyricist/arranger ids
Updates the MusicBrainz plugin to also import MBIDs for composers/lyricists/arrangers, and also adds them as multi-valued fields.
2026-04-09 10:40:58 +02:00
Šarūnas Nejus
4e08403df4 feat(fetchart): add support for webp files (#6511)
Add support for webp images in the fetchart plugin.
2026-04-08 18:39:54 +01:00
Mathilde Gilles
e49398b2f5 Merge branch 'master' into fetchart-webp 2026-04-08 16:38:02 +02:00
Šarūnas Nejus
2dff447e3b listenbrainz: Add pagination, play count aggregation, and recording_mbid fix (#6484)
Follow-up to #6471 — fixes three remaining issues with the
`listenbrainz` plugin:
- **Aggregate listen events into actual play counts.** ListenBrainz
returns individual listen events, each mapped to `playcount: 1`. Without
aggregation, the final `listenbrainz_play_count` is always 1 regardless
of actual listens.
- **Paginate through all listens.** The API defaults to 25 results per
request. Now fetches up to 1000 per page and loops via `max_ts` until
all listens are retrieved.
- **Use `recording_mbid` from `mbid_mapping` when present.** Previously
`mbid` was left as `None` when the mapping existed, falling back to
expensive MB API lookups unnecessarily.

  Fixes #6469 (remaining issues after #6471)
2026-04-08 15:19:35 +01:00
Alok Saboo
193f86185e listenbrainz: update changelog to clarify MB lookup removal 2026-04-08 10:05:02 -04:00
Alok Saboo
17ffd18b77 playcount: batch all store() calls in a single transaction
Each song.store() was opening and committing its own SQLite transaction.
With thousands of unique tracks the WAL grows and each successive write
becomes slower. Wrapping the loop in a single transaction makes writes
O(1) per item instead of progressively slower.
2026-04-08 09:59:41 -04:00
Alok Saboo
8c5c89a844 playcount: log progress every 250 tracks during processing 2026-04-08 09:02:51 -04:00
Alok Saboo
cb8d3abe8d listenbrainz: add --max option and drop per-listen MB lookups
- Add `beet lbimport --max=N` to cap the number of listens fetched.
- Remove the MusicBrainz API lookup from get_tracks_from_listens.
  Previously, every listen without a recording_mbid in the API mapping
  triggered a live MB search, causing the import to hang for hours on
  large listen histories. Matching falls back to artist/title/album
  which is already handled by update_play_counts.
2026-04-08 08:34:48 -04:00
ShimmerGlass
3194666610 feat(fetchart): add support for webp files 2026-04-08 12:39:58 +02:00
Alok Saboo
1ae8f90555 listenbrainz: respect X-RateLimit headers during pagination
Sleeps when the rate-limit window is exhausted so bulk fetches
(many pages of listens) don't get throttled by the server.
2026-04-07 20:47:56 -04:00
Alok Saboo
735695e90a lint 2026-04-07 20:37:15 -04:00
Alok Saboo
af8d716b82 address reviewer comments 2026-04-07 20:37:15 -04:00
Alok Saboo
5ac984223d lint 2026-04-07 20:37:14 -04:00
Alok Saboo
0fe518245e lint 2026-04-07 20:36:36 -04:00
Alok Saboo
6c5519c775 add changelog 2026-04-07 20:36:34 -04:00
Alok Saboo
9e02536bee ListenBrainz: aggregate play counts and improve pagination logic 2026-04-07 20:31:14 -04:00
Šarūnas Nejus
7b0e4e2d87 fix(fetchart): sources definition (#6508)
## Description

The fetchart plugin would silently drop unknown sources defined in
config, leading to hard to debug problems.
The plugin now errors when an unknown source is configured, or when no
sources are configured.
In addition, a single string is now a valid value for `sources` to
either enable all sources with an `*`, or a single source.

Fixes: #6336
2026-04-07 18:11:16 +01:00