Commit graph

12509 commits

Author SHA1 Message Date
Sebastian Mohr
b4a634a443
Allow to pickle db models by removing the current connection. (#5641)
## Description

This might be a quick one, depending on how you feel about it... It
allows you to pickle DB model objects. I don't think this is used
directly in Beets, but it might be useful in general. For instance, we
encountered an issue where we wanted to quickly pickle an Item or Album.
This sometimes worked and other times failed, which seemed quite
inconsistent.

Some DB model methods and properties have the side effect of attaching
an SQLite connection to self (._db), which prevents serialization. The
fix is quite straightforward, so I thought we might want to integrate
this into beets directly.

## To Do

- [x] Changelog
- [x] Tests
2025-04-15 11:43:36 +02:00
Benedikt
98014ab02e
Ftintitle typing (#5727)
some work following up my review of https://github.com/beetbox/beets/pull/5718
- add typings
- refactor the logic in the core `ft_in_title` function, motivated by the
fact that I found this more difficult to read than necessary during the
review
2025-04-15 09:51:52 +02:00
wisp3rwind
b495286127 ftintitle: flatten code
linear code with early exits instead of more complicated nested
conditionals
2025-04-14 20:50:01 +02:00
wisp3rwind
8413de1a85 ftintitle: add typings 2025-04-14 20:42:59 +02:00
Peter Dolan
447cc82e04
Do not write unchanged items to the library in FtInTitle (#5718)
FtInTitle performs a library store operation for every item it
processes, whether or not the item has changed. By limiting the
`item.store()` call to only those cases when the item has changed, the
plugin’s performance when processing an entire library improves by two
to three orders of magnitude.
2025-04-14 18:22:41 +00:00
Šarūnas Nejus
d447bc3f46
mbsync / missing plugins: consider all metadata backends (#5636)
This PR adds support for alternate metadata backends in the `mbsync` and
`missing` plugins, allowing them to work with any metadata backend the
items are tagged with instead of being limited to just MusicBrainz.

Key changes:
- Removed redundant `albums_for_id` and `items_for_id` methods.
- Updated `album_for_id` and `track_for_id` implementations to return a
single album/track as their names suggest.
- Updated both plugins to use the above functions instead of
MusicBrainz-specific ones.
- Updated `missing` plugin docs to make it clear that missing albums for
artists can only be obtained for the `musicbrainz` metadata backend
- This is possible with other backends but implementation was outside of
this PR's scope.
  - Slightly simplified the existing implementation

The changes maintain backwards compatibility while enabling users to
leverage metadata from sources like Deezer, Discogs or Bandcamp when
syncing their libraries.

**Note**: this change came about because I'm working on making
`musicbrainz` a separate plugin: this will be submitted in the next PR.
2025-04-14 04:10:32 +01:00
Šarūnas Nejus
8f209d85b9
Tidy up mbsync logs
No need to call `format` since this is done automatically at the point
the object is logged (if required).
2025-04-14 02:58:58 +01:00
Šarūnas Nejus
d1d681c1ff
mbsync: support other data sources 2025-04-14 02:28:43 +01:00
Šarūnas Nejus
441cd36e8a
missing: clarify that only musicbrainz backend supports missing albums for artist
And give this functionality a small refactor.
2025-04-14 02:28:43 +01:00
Šarūnas Nejus
4c1f217ce0
missing: support non-musicbrainz data sources 2025-04-14 02:28:42 +01:00
Šarūnas Nejus
6a192d0bdb
albums_for_id -> album_for_id and return a single candidate instead of an iterator 2025-04-14 02:28:42 +01:00
Šarūnas Nejus
69b12a337f
plugins/thumbnails: fix FFI with GIO on s390x (#5708)
Using the correct function signature for g_file_new_for_path fixes the
tests on s390x.
I do not have the full story on why this failed consistently only on
s390x, but I guess the big endian might have something to play with
this.

Here is how the tests were failing:
```
169s ___________________________ ThumbnailsTest.test_uri ____________________________
169s
169s self = <test.plugins.test_thumbnails.ThumbnailsTest testMethod=test_uri>
169s
169s     def test_uri(self):
169s         gio = GioURI()
169s         if not gio.available:
169s             self.skipTest("GIO library not found")
169s
169s >       assert gio.uri("/foo") == "file:///"  # silent fail
169s E       AssertionError: assert '' == 'file:///'
169s E
169s E         - file:///
169s
169s test/plugins/test_thumbnails.py:268: AssertionError
```
You can see a full log here [1] and a history of consistent failure here
[2]. Both links are bound to expire at some point, sorry future
archeologist 🤷.

[1]:
https://autopkgtest.ubuntu.com/results/autopkgtest-plucky/plucky/s390x/b/beets/20250403_162414_5d1da@/log.gz#S5
[2]: https://autopkgtest.ubuntu.com/packages/beets/plucky/s390x
2025-04-14 02:23:26 +01:00
Šarūnas Nejus
f4de44f610 Add plugin prefix in changelog note 2025-04-14 02:19:10 +01:00
Skia
225c21b90f plugins/thumbnails: fix FFI with GIO on s390x
Using the correct function signature for g_file_new_for_path fixes the
tests on s390x.
I do not have the full story on why this failed consistently only on
s390x, but I guess the big endian might have something to play with
this.

Here is how the tests were failing:
```
169s ___________________________ ThumbnailsTest.test_uri ____________________________
169s
169s self = <test.plugins.test_thumbnails.ThumbnailsTest testMethod=test_uri>
169s
169s     def test_uri(self):
169s         gio = GioURI()
169s         if not gio.available:
169s             self.skipTest("GIO library not found")
169s
169s >       assert gio.uri("/foo") == "file:///"  # silent fail
169s E       AssertionError: assert '' == 'file:///'
169s E
169s E         - file:///
169s
169s test/plugins/test_thumbnails.py:268: AssertionError
```
You can see a full log here [1] and a history of consistent failure
here [2]. Both links are bound to expire at some point, sorry future
archeologist 🤷.

[1]: https://autopkgtest.ubuntu.com/results/autopkgtest-plucky/plucky/s390x/b/beets/20250403_162414_5d1da@/log.gz#S5
[2]: https://autopkgtest.ubuntu.com/packages/beets/plucky/s390x
2025-04-14 02:19:10 +01:00
Sebastian Mohr
5a79c6f23e
Merge branch 'master' into typehints-plugins 2025-04-10 11:05:30 +02:00
J0J0 Todos
cd19837e50
Fix lastgenre returning empty genre instead of fallback genre (#5682)
Fixes #5649, more precisely the behavior described in comment
https://github.com/beetbox/beets/issues/5649#issuecomment-2732245358
where a last.fm genre was found for the album but `_resolve_genre()`
kicked it out because it was't allowed by the whitelist which resulted
in writing an empty genre as the final result.

- This fix makes sure that when no album genre is found the next stage
is hit - returning with either the original genre (if any), the
configured fallback genre or None.

- A new option `--debug` or configuration setting `extended_debug: yes`
can be set to see precisely what tags were fetched during the track,
album or artist stages prior to filtering by a whitelist or
canonicalization tree. It's not enabled by default since it clutters up
the debug log quite a bit especially during *Various Artists* checks,
trying to get the most popular track genre.

Some refactorings to make the plugins behavior better readable and also
fixing some regressions:

- `_resolve_genres` returns a list and so does `_combine_genres` (which
is now renamed to `combine_resolve_and_log` which more precisely
reflects what it's doing).
- _`to_delimited_genre_string` included reducing to count *prevsiously*,
which was kind of hidden in there and not obvious on first sight and
also it did some formatting. The name was bad! It is now called
`format_and_stringify` (the count reduction part moved to
`_resolve_genres`)
- Since `_resolve_genre` does count-reduction when canonicalization is
configured, it makes sense to also do that when only whitelist checks
are done.
- New log message introduced in `_combine_resolve_and_log`showing the
list of existing genres that are taken into account before
whitelist/canonicalization does its magic.
- `_resolve_genre` got a docstring describing what it's doing 
- New helper `_filter_valid_genres` now used in `_resolve_genre`
(instead of hardcoded is-valid-list-comp) and other places
(`fetch_genre`)
- `fetch_genre` now also does a valid check. That way it is assured that
if everything was kicked, the next stage is entered (eg. album ->
artist)
- This fallback to next stage is now tested with a new `test_get_genre`
test-case.
2025-04-09 23:01:06 +02:00
J0J0 Todos
a79dd3849a lastgenre: Better describe fallback option in docs 2025-04-09 22:52:07 +02:00
J0J0 Todos
13f1bec437 lastgenre: Improve force option definition in docs 2025-04-09 22:52:07 +02:00
J0J0 Todos
eb83491788 lastgenre: Fix "original fallback" conditions
This was not thought through clearly before. It now behaves as follows
which I suppose is least surprising to a user:

- force is on, keep_existing is on, but the whitelist is DISABLED
- no stage found anything on last.fm
- fall back to the original genre

If in this example the whitelist would be ENABLED, the behaviour
changes: Only if the existing genre passes the whitelist test the
original is kept.
2025-04-09 22:52:07 +02:00
J0J0 Todos
de9020055d lastgenre: Add test ensuring next stage is hit
If no album was found the next stage (artist) should be entered, the
original genre kicked out (not whitelisted) and artist genre accepted.
The log message is slightly misleading since it tried to keep existing
genres but they were not whitelisted, thus kicked out. This is expected.
2025-04-09 22:52:07 +02:00
J0J0 Todos
68202f3498 lastgenre: Document extended_debug setting 2025-04-09 22:52:07 +02:00
J0J0 Todos
a3ce7c4d70 lastgenre: Format a docs paragraph as "notes box"
unrelated fix in this PR...
2025-04-09 22:52:07 +02:00
J0J0 Todos
f4d22a83b5 lastgenre: Catch NoneType errors in _fitler_valid_genres 2025-04-09 22:52:07 +02:00
J0J0 Todos
702ddf493e lastgenre: Early validate genres, new debug option
- Revert/fix last.fm fetcher methods to validate genres.

  - In past versions (<=2.2) _resolve_genres which included whitelist
    checks ran instantly after fetching last.fm tags which made sure the
    next stage is hit when nothing worthwhile was found (e.g fallback
    album -> artist).

  - Bring back this behavior but don't run a full _resolve_genres but a
    quick valid (whitelist) check only!

- Introduce an extended config/CLI option that allows to really log what
  each stage fetches (prior to validation/whitelist filtering).

  - Since this potentially is verbose especially with VA albums (a lot
    of artist tag fetches) for performance and debug log clutter reasons
    this is disabled by default.

- Clarify final last.fm tags debug log message to "valid last.fm genres"
2025-04-09 22:52:07 +02:00
J0J0 Todos
c57e5a1fb8 lastgenre: Tiny fix in early no-force return 2025-04-09 22:52:07 +02:00
J0J0 Todos
edd366e766 lastgenre: Fix album not falling back to artist stage
which was the usual behaviour in lastgenre ever since and it should be
kept that way. Also refactor "if track" to use a similar notation for
overall code readability.
2025-04-09 22:52:07 +02:00
J0J0 Todos
2b276e07f1 lastgenre: Log unconfigured fallback 2025-04-09 22:52:07 +02:00
J0J0 Todos
e20cf7f20b lastgenre: Rework combine, stringify, count reduction
- Rename method from _combine_genres() to _combine_resolve_and_log() to
  make clear that it not only combines new and old genres but also
  resolves them (which in this plugin's wording means "do the magic" of
  canonicalizationm, whitelist checking and reducing to a configured
  genre count).

- Clarify in _resolve docstring that a possible outcome might be all
  genres being removed.

- Add an additional log message telling which existing genres are taken
  into account BEFORE "the magic happens".

- Rename _to_delimited_genre_string() to _format_and_stringify()

- Move count reduction logic to _resolve_genres()

- Fix and rename a test
2025-04-09 22:52:07 +02:00
J0J0 Todos
3291aa03e7 lastgenre: Describe in docstring what _resolve_genres does 2025-04-09 22:52:07 +02:00
J0J0 Todos
15f4b2ac29 lastgenre: Place to_delim. func near _get_genre
and the other helpers.
2025-04-09 22:52:07 +02:00
J0J0 Todos
eba3dc15fd lastgenre: Final label only if required in _get_genre() 2025-04-09 22:52:07 +02:00
J0J0 Todos
94f78ae70f lastgenre: Prevent returning empty genre list
As reported in #5649 when new last.fm genres were found, they still might
get kicked out by the whitelist check in _resolve_genres(). This might
lead to _combine_genres() returning an empty list.

The desired outcome though is that since still nothing worthwhile was
found, the next stage should be entered - which in this case is,
returning with the configured fallback genre (or the default fallback
None).

The any() check makes sure this is the case and moving out the
string conversion from _combine_genres() makes this code slightly more
readable.
2025-04-09 22:52:07 +02:00
Sebastian Mohr
559b87ee56 Updated changelog.
Also edited my previous changelog entry to streamline
typehint entries a bit.
2025-04-08 18:29:54 +02:00
Sebastian Mohr
753bdd9106 Added typehints for _instance, _classes and for class attributes. 2025-04-08 18:15:08 +02:00
Sebastian Mohr
024e13bc1a
Merge branch 'master' into typehints-plugins 2025-04-08 15:45:15 +02:00
Šarūnas Nejus
1cec93e695
(docs) Fix Broken link for Arch Linux package (#5688) 2025-04-08 14:38:45 +01:00
racehd
0f2b8b794a Fix Broken link for Arch Linux package 2025-04-08 15:33:43 +02:00
Benedikt
6895a946e5
Fix typing_extensions requirement inconsistency (#5700)
Typing Extensions is an optional requirement for `python>3.10`, thus
TypeVarTuple and Unpack need another import in higher python versions as
`typing_extensions` is not available

see https://docs.python.org/3.10/library/typing.html for supported types

close #5695
2025-04-08 15:20:30 +02:00
Sebastian Mohr
58ab2c696a Removed typevar from typing_extensions as it is available in typing since
like python 3.3
2025-04-02 17:25:53 +02:00
Sebastian Mohr
e6ce818913 Added some more typehints where missing. 2025-04-02 16:28:41 +02:00
Sebastian Mohr
19b3330fc8 Fixed circular import of distance by consistently importing if whenever
it is needed.
2025-04-02 16:07:00 +02:00
Sebastian Mohr
0cc0db313a Added typehints to the plugins file. 2025-04-02 15:57:16 +02:00
Sebastian Mohr
bcae6429a4 typing extensions is an optional requirement for py>3.10, therefore
TypeVarTuple and Unpack need additional handling see
https://docs.python.org/3.9/library/typing.html

see #5695
2025-04-02 14:43:38 +02:00
Šarūnas Nejus
030fd1fcf5
Update README.rst (#5691)
Fixes #5669

SCREENSHOT ATTACHED-

![image](https://github.com/user-attachments/assets/76bc254c-0ce6-4b56-9705-ff39a0b67502)
2025-03-27 04:22:26 +00:00
Unnati
ecbb0edd15
Update README.rst 2025-03-27 09:23:09 +05:30
Unnati
656dcf1a9d
Merge branch 'master' into Unnati-techie-updated-broken-img-readme 2025-03-27 09:13:46 +05:30
Šarūnas Nejus
19ee164560
Update python in CI (#5544)
Bump python version in the CI to current versions.

This is the same as #5503 that for some reason got closed when the merge
of the `drop-py38` branch happened
2025-03-27 02:23:53 +00:00
Unnati
a48683bb62
Update README.rst 2025-03-26 20:32:39 +05:30
Fabio Alessandro Locati
4b04d0e7c8
Update python to current supported versions. 2025-03-24 22:12:58 +01:00
Bob Cotton
670a3bcd17
Add beets-id3extract to community plugins (#5660)
Add link to community plugin beets-id3extract
2025-03-12 07:14:38 +00:00