Commit graph

12644 commits

Author SHA1 Message Date
Šarūnas Nejus
5128a817be
Update missed out python version in the build 2025-05-07 10:24:04 +01:00
Šarūnas Nejus
a40a3d45e4
Install docs dependencies early 2025-05-07 10:24:02 +01:00
Šarūnas Nejus
e1101b7a38
pipeline: remove old tests, integrate with our test suite (#5729)
These have probably not been run by anyone in ages, better to move the
code to our test suite where it is regularly exercised. In fact, the
latter covers most of the cases already. The only missing tests seem to
be those were exceptions are raised in the first or last stage. Thus,
this adds such tests.
2025-05-06 13:58:09 +01:00
wisp3rwind
2c68349314 pipeline: remove old tests, integrate with out test suite
These have probably not been run by anyone in ages, better to move the
code to our test suite where it is regularly exercised. In fact, the
latter covers most of the cases already. The only missing tests seem to
be those were exceptions are raised in the first or last stage. Thus,
this adds such tests.
2025-05-06 14:51:20 +02:00
Benedikt
9756a5d061
Fix _legalize_path types and clean up path legalisation logic (#5224)
**Refactor: Simplify Path Generation and Legalization**

This PR refactors the way destination paths for library items are
generated and made filesystem-safe. The goal is to simplify the process,
make it more robust, and centralize most of the path manipulation logic.

**Key Changes:**

*   **`Item.destination` Simplified:**
* The method now has a clearer interface, primarily controlled by the
`relative_to_libdir` flag (replacing the old `fragment` flag).
* It consistently returns the path as `bytes`, ready for filesystem
operations.
* Path legalization logic (sanitization, truncation, replacements) is
now delegated to `util.legalize_path`.

*   **`util.legalize_path` Enhanced:**
* Takes responsibility for the full legalization process, including
truncation based on filesystem limits.
* Uses new helper functions (`util.truncate_path`, `util.truncate_str`)
for robust truncation of path components while preserving extensions and
handling multi-byte characters correctly.
* Includes logic to handle potential conflicts where
sanitization/replacements might interfere with truncation, falling back
to default rules if necessary.

*   **Centralized Max Length:**
* A new cached function `util.get_max_filename_length` determines the
maximum filename length, checking the config first and then querying the
filesystem.

This refactoring leads to cleaner code by separating concerns:
`Item.destination` focuses on generating the *intended* path based on
metadata and formats, while `util.legalize_path` and its helpers handle
the complexities of making that path valid for the target filesystem.
2025-05-06 14:49:51 +02:00
Šarūnas Nejus
921b7ed9ea
Rewrite legalisation tests for readability 2025-05-04 12:23:26 +01:00
Šarūnas Nejus
6a7832f207
Adjust tests to work in Windows 2025-05-04 12:23:26 +01:00
Šarūnas Nejus
5826d6b59b
Remove handling of mbcs encoding
This has been phased out in Python 3.6.

https://peps.python.org/pep-0529/
2025-05-04 12:23:26 +01:00
Šarūnas Nejus
4fcb148d60
Add test for legalization logic 2025-05-04 12:23:26 +01:00
Šarūnas Nejus
40fbc8ee7e
Fix and simplify path truncation 2025-05-04 12:23:26 +01:00
Šarūnas Nejus
b3fd84b356
Move max filename length calculation closer to where it is used 2025-05-04 12:23:25 +01:00
Šarūnas Nejus
d3186859ac
Remove unused platform parameter in item.destination 2025-05-04 12:23:25 +01:00
Šarūnas Nejus
06dd2738bb
Remove never used replacements paramater from item.destination 2025-05-04 12:23:25 +01:00
Šarūnas Nejus
52951bf719
Fix legalize_path types
Background
  The `_legalize_stage` function was causing issues with Mypy due to
  inconsistent type usage between the `path` and `extension` parameters.
  This inconsistency stemmed from the `fragment` parameter influencing the
  types of these variables.

Key issues
  1. `path` was defined as `str`, while `extension` was `bytes`.
  2. Depending on `fragment`, `extension` could be either `str` or `bytes`.
  3. `path` was sometimes converted to `bytes` within `_legalize_stage`.

Item.destination` method
  - The `fragment` parameter determined the output format:
    - `False`: Returned absolute path as bytes (default)
    - `True`: Returned path relative to library directory as str

Thus
  - Rename `fragment` parameter to `relative_to_libdir` for clarity
  - Ensure `Item.destination` returns `bytes` in all cases
  - Code expecting strings now converts the output to `str`
  - Use only `str` type in `_legalize_stage` and `_legalize_path`
    functions
  - These functions are no longer dependent on `relative_to_libdir`
2025-05-04 12:23:22 +01:00
Aidan Epstein
ecdff785f7
Only output verbose details for parentwork plugin when running explicitly (#5135)
Fixes #4120.
2025-05-04 09:34:37 +02:00
Benedikt
5d010e95ec
Use os.fsencode to encode bytes (#5662)
# Remove Python 2 compatibility code for string encoding/decoding

This PR simplifies the codebase by removing Python 2 compatibility code
related to string encoding and decoding operations. Key changes:

- Remove custom `_convert_args`, `arg_encoding()`, `_fsencoding()` and
`convert_command_args()` functions
- Replace with standard library's `os.fsencode()` and `os.fsdecode()`
for file system path handling
- Simplify bytestring/string conversions throughout the codebase
- Update test code to use modern string handling

These changes reduce code complexity and maintenance burden since Python
2 support is no longer needed.
2025-04-22 11:56:53 +02:00
Šarūnas Nejus
9acfa3c175
Remove arg_encoding 2025-04-21 12:41:57 +01:00
Šarūnas Nejus
179ed13e09
Say bye to util._fsencoding 2025-04-21 12:41:57 +01:00
Šarūnas Nejus
980199602e
Encode paths using os.fsencode 2025-04-21 12:41:57 +01:00
Benedikt
43301c4caa
fixup #5701 (#5745)
which added some mypy config to pyproject.toml, leading to mypy ignoring
setup.cfg
 (this shows up in CI output for #5701, but is not very visible
since we currently ignore mypy errors)

Related: https://github.com/beetbox/beets/pull/5728
2025-04-20 10:43:10 +02:00
Šarūnas Nejus
c5bfbde175
Added typehints to the plugins file. (#5701)
Added some typehints to the `plugins.py` file. There are some typehints
missing but I think this is a good first step to get a better DevEx.
2025-04-19 20:20:30 +01:00
Sebastian Mohr
6594bd7f24 Moved all typehint that need a typechecking guard to the top. see
https://github.com/beetbox/beets/pull/5701#discussion_r2051486012
2025-04-19 16:19:07 +02:00
Sebastian Mohr
557cb1a019 minor additions 2025-04-19 13:00:12 +02:00
Sebastian Mohr
2f57dd9e1c Added missing return types. 2025-04-18 23:03:32 +02:00
Sebastian Mohr
d7838b29c3 https://github.com/beetbox/beets/pull/5701#discussion_r2050637901 2025-04-18 16:03:39 +02:00
Sebastian Mohr
39a5bdb0bd https://github.com/beetbox/beets/pull/5701#discussion_r2050488475 2025-04-18 13:29:33 +02:00
Sebastian Mohr
fef81af67d https://github.com/beetbox/beets/pull/5701#discussion_r2048644522 2025-04-18 12:06:02 +02:00
Sebastian Mohr
b3c61d5c19 typed import_stages 2025-04-17 14:17:45 +02:00
Sebastian Mohr
d7c1d7f1a1
Merge branch 'master' into typehints-plugins 2025-04-16 16:03:46 +02:00
Benedikt
3c177b56fb
remove outdated advice from Python 2 times in CONTRIBUTING.rst (#5733) 2025-04-16 10:51:09 +02:00
Sebastian Mohr
62d28260c7 small nits 2025-04-15 18:04:40 +02:00
Sebastian Mohr
287c7228af Fixed circular import issue introduced in last commit 2025-04-15 12:02:40 +02:00
Sebastian Mohr
90254bb511 Fixed lint error introduced by merging main. 2025-04-15 11:57:52 +02:00
Sebastian Mohr
f878e4da3e
Merge branch 'master' into typehints-plugins 2025-04-15 11:52:08 +02:00
Sebastian Mohr
7e61027366 Added suggestions from code review 2025-04-15 11:49:24 +02:00
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