Commit graph

4193 commits

Author SHA1 Message Date
Šarūnas Nejus
e151b4b49b
Implement track_for_id to allow fetching singletons by discogs id 2025-05-19 09:43:56 +01:00
Šarūnas Nejus
d9b67acff5
discogs: simplify getting track from album 2025-05-19 09:43:03 +01:00
Šarūnas Nejus
9cc7ecaceb
discogs: cache TRACK_INDEX_RE 2025-05-19 09:43:03 +01:00
Šarūnas Nejus
8e5858254b
discogs: cache master release lookups 2025-05-19 09:43:03 +01:00
Šarūnas Nejus
12149b3e6d
discogs: remove duplicate error handling 2025-05-19 09:43:03 +01:00
Šarūnas Nejus
09862aeaea
discogs: Add types to public methods 2025-05-19 09:43:01 +01:00
Šarūnas Nejus
9242db04a5
discogs: add configurable search_limit 2025-05-19 09:42:06 +01:00
Šarūnas Nejus
e8e9369bc7
Remove unused extra_tags parameter 2025-05-19 09:18:06 +01:00
Šarūnas Nejus
2ec65ed8ca
Deduplicate candidate methods using _search_api method 2025-05-18 20:09:52 +01:00
Šarūnas Nejus
0102f3ce7d
Take into account va_likely param and remove redundant checks
- Instead of checking for empty `artist` query, use `va_likely`
  parameter to determine whether we should query for Various Artists or
  not.
- `album` / `title` is always a truthy string - no need to handle empty
  criteria case
- `tracks` list always has at least one track - no need to check for
  `len(items)`
2025-05-18 20:09:52 +01:00
Šarūnas Nejus
6487893315
Synchronise docs with the actual supported extra_tags 2025-05-18 20:09:51 +01:00
Šarūnas Nejus
be74936134
Handle extra_tags the way they used to be handled 2025-05-18 20:09:50 +01:00
Martin Atukunda
7c799beda8
style(plugin/web): run djlint over html to clean it up a bit 2025-05-18 20:59:29 +03:00
Martin Atukunda
992d376d1b
feat(plugin/web): add artist and album to the item entry template 2025-05-18 20:59:29 +03:00
Martin Atukunda
a75d2b4aa6 Update beetsplug/web/static/beets.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-18 17:16:29 +01:00
Martin Atukunda
d1d58569e1 Update beetsplug/web/static/beets.js
don't pollute global scope with album_id

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-18 17:16:29 +01:00
Martin Atukunda
b0238d934e feat: plugins/web: use media session api for notifications.
The Media Session API provides a way to customize media notifications.
This commit updates the metadata for the media session whenever a new
track (item) starts playing.

https://developer.mozilla.org/en-US/docs/Web/API/Media_Session_API
2025-05-18 17:16:29 +01:00
Šarūnas Nejus
b520981c9c
plugins: restructure id extraction 2025-05-17 14:57:34 +01:00
Sebastian Mohr
a2e316d444 Merge remote-tracking branch 'upstream/master' into importer-restructure 2025-05-17 10:32:50 +02:00
Šarūnas Nejus
f1dc75f743
Fix types in all edited files 2025-05-17 03:32:01 +01:00
Šarūnas Nejus
7ff73d9747
musicbrainz: set default config in the code 2025-05-17 03:31:59 +01:00
Šarūnas Nejus
a538a3a150
musicbrainz: move handling of extra tags to musicbrainz plugin 2025-05-17 03:31:58 +01:00
Šarūnas Nejus
de0958ca65
Use candidate function from plugins instead of hooks 2025-05-17 03:31:57 +01:00
Šarūnas Nejus
2fcb48d7a4
Remove ...for_mbid methods and simplify the rest 2025-05-17 03:31:57 +01:00
Šarūnas Nejus
d8067b219b
musicbrainz: use self.config and self._log 2025-05-17 03:31:57 +01:00
Šarūnas Nejus
fd62d6a0b8
Integrate functionality with BeetsPlugin shenanigans 2025-05-17 03:31:57 +01:00
Šarūnas Nejus
529aaac7dc
Move functionality under MusicBrainzPlugin 2025-05-16 23:08:38 +01:00
Šarūnas Nejus
06dde6e37e
Define MusicBrainzPlugin 2025-05-16 20:17:59 +01:00
Šarūnas Nejus
5dc6f45110
musicbrainz: reorder methods
This will make it easier to track changes in later commits.
2025-05-16 19:56:50 +01:00
Šarūnas Nejus
e6e610a3ef
Move musicbrainz to beetsplug directory 2025-05-16 19:56:50 +01:00
Sebastian Mohr
68acaa6470 Renamed all action occurrences with Action. 2025-05-13 13:01:46 +02:00
Šarūnas Nejus
fdc1aba603
Replace typing.cast with explicit type definitions and ignore TC006 2025-05-07 20:39:33 +01:00
Šarūnas Nejus
99dc0861c2
Redact sensitive fields
Redacted fields reported by GitHub secrets scanner[1] and a couple of others.

1: https://github.com/beetbox/beets/security/secret-scanning?query=is%3Aclosed
2025-05-07 20:39:32 +01:00
Šarūnas Nejus
c490ac5810
Fix formatting 2025-05-07 10:41:01 +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
Š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
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
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
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
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
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
Allen
b7521f9a0b
fix: plugins/listenbrainz: Fix UnboundLocalError in cases where 'mbid' is not defined (#5651)
Fix ocurrence of `UnboundLocalError` in plugins/listenbrainz >
`get_tracks_from_listens()` when `mbid` is not available.
Removed a print statment.
Fix link to config.yaml.
Fix link to Listenbrainz "get the token" documentation.

Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2025-03-12 07:08:53 +00:00
Pierre Ayoub
5c8f1c1ee5
Fix convert plugin attempting to process a non-media file (#5261)
## Description

My library is managed using Beets for organization and
[git-annex](https://git-annex.branchable.com/) as storage backend.
Therefore when using this system, while my library files always exists
on my filesystem, some files may be empty (without content). In this
case, when I'm running the `convert` plugin, I don't wants it to process
files which are empty (same apply for any Beets plugin). Hence, I added
a check that the file is readable as a `MediaFile` before doing any
process.

Before this fix, trying to encode an empty file would have lead to an
error while leaving `convert` doing its side-effects **and** `convert`
would also copy empty files to destination for files that doesn't need
to be re-encoded.

In my case, this is empty files, but the problem can be anything else
(depending on the storage backend) and/or corrupted files. Conclusion, I
think **checking that the file is readable is always recommended before
proceeding to heavy operation** like this.
2025-02-20 16:23:14 +00:00
Šarūnas Nejus
b713d72612
translations: use a more distinctive separator
I found that the translator would sometimes replace the pipe character
with another symbol (maybe it got confused thinking the character is
part of the text?).

Added spaces around the pipe to make it more clear that it's definitely
the separator.
2025-02-20 03:47:04 +00:00
Šarūnas Nejus
43032f7bc7
translations: make sure we do not re-translate 2025-02-20 03:47:04 +00:00
Šarūnas Nejus
7893766e4c
Improve flags structure and add tests 2025-02-20 03:47:04 +00:00
Šarūnas Nejus
c95156adcd
Refactor writing rest files 2025-02-20 03:47:04 +00:00
Šarūnas Nejus
d7201062a8
Resurrect translation functionality 2025-02-20 03:47:04 +00:00
J0J0 Todos
2286511ebe
Merge branch 'master' into smartplaylist-attr-url-encoding 2025-02-17 21:16:09 +01:00
Max Goltzsche
5d96509cfe
smartplaylist: change encoding of additional field
URL-encode additional item `fields` within generated EXTM3U playlists instead of JSON-encoding them.
This is because JSON-encoding additional fields/attributes made it difficult to parse the `EXTINF` line but using URL-encoding for these values makes parsing easy (because URL-encoded values cannot contain commas, quotation marks and spaces).

I introduced the generation of additional EXTM3U item fields earlier this year and I want to correct that now.

**Design/definition background:**
Unfortunately, I didn't find a clear definition of how additional playlist item attributes should be encoded - apparently there is none.
Given that item URIs within an M3U playlist can be URL-encoded already, defining the values of additional attributes to be URL-encoded is consistent design.
I didn't find examples of additional EXTM3U item attributes in the web where the attribute value contains a comma, space or quotation mark but examples that specified numeric IDs and URLs as attribute values.
Because the URL attribute examples I found didn't contain URL-encoded characters and because it is more readable and unproblematic for parsing, I've let the attribute URL encoding treat `:` and `/` as safe characters.

**Breaking change:**
While this is a breaking change in theory, in practice it is not since afaik all integrations of the smartplaylist plugin's additional EXTM3U item attribute generation feature (beets-webm3u) work with simple attribute values such as the item ID (numeric) whose formatting/encoding is not affected when changing from JSON to URL-encoding.
In other words the change is backward-compatible with the beets-webm3u plugin (which I'll adjust correspondingly after this beets PR was merged).
2025-02-01 01:14:27 +01:00
Šarūnas Nejus
916d40f86f
Remove outdated namespace package definition and update docs
See https://realpython.com/python-namespace-package.

This setup is backwards-compatible, so plugins using the old
pkgutil-based setup will continue working fine.

This setup has an advantage where external plugins will now be able to
import modules from 'beetsplug' package for typing purposes. Previously,
mypy could not resolve these modules due to presence of `__init__.py`.
2025-01-30 12:20:11 +00:00
Šarūnas Nejus
dab9a0d7c4
Bring back Tekstowo search
It was my mistake to remove search earlier - I found that in many cases
it works fine.
2025-01-27 10:56:54 +00:00
Šarūnas Nejus
7389f241f4
Do not search for Various Artists, split titles by ' / ' 2025-01-27 10:56:53 +00:00
Šarūnas Nejus
39c479fcab
Google: add support for dainuzodziai.lt 2025-01-27 10:56:53 +00:00
Šarūnas Nejus
734bcc28a8
Append source to the lyrics 2025-01-27 10:56:53 +00:00
Šarūnas Nejus
bdc564a573
Tidy up handling of backends 2025-01-27 10:56:53 +00:00
Šarūnas Nejus
04054cac5c
Remove dependency existence checks
I think we can make our life easier by removing these checks assuming
that users follow the instructions in the docs.
2025-01-27 10:56:53 +00:00
Šarūnas Nejus
b2402b1634
Google: make sure we do not return the captcha text
If we get caught by Cloudfare, it forwards our request somewhere else
and returns some validation text response. To make sure that this text
does not get assumed for lyrics, we can disable redirects for the Google
backend, check the response code and raise if there's a redirect
attempt. This source will then be skipped and the backend continues with
the next one.
2025-01-27 10:56:53 +00:00
Šarūnas Nejus
07d372c13d
Google: prioritise Songlyrics and AZlyrics sources 2025-01-27 10:56:53 +00:00
Šarūnas Nejus
70554640e5
Create Html class for cleaning up the html text
Additionally, improve HTML pre-processing:

* Ensure a new line between blocks of lyrics text from letras.mus.br.
* Parse a missing last block of lyrics text from lacocinelle.net.
* Parse a missing last block of lyrics text from paroles.net.
* Fix encoding issues with AZLyrics by setting response encoding to
  None, allowing `requests` to handle it.
2025-01-27 10:56:52 +00:00
Šarūnas Nejus
c5c4138d66
Google: Refactor and improve
* Type the response data that Google Custom Search API return.
* Exclude some 'letras.mus.br' pages that do not contain lyric.
* Exclude results from Musixmatch as we cannot access their pages.
* Improve parsing of the URL title:
  - Handle long URL titles that get truncated (end with ellipsis) for
    long searches
  - Remove domains starting with 'www'
  - Parse the title AND the artist. Previously this would only parse the
    title, and fetch lyrics even when the artist did not match.
* Remove now redundant credits cleanup and checks for valid lyrics.
2025-01-27 10:56:52 +00:00
Šarūnas Nejus
12c5eaae5e
Unite Genius, Tekstowo and Google backends under the same interface 2025-01-27 10:56:52 +00:00
Šarūnas Nejus
745c5eb9f0
Genius: refactor and simplify 2025-01-27 10:56:52 +00:00
Šarūnas Nejus
54fc67b30a
Remove extract_text_between 2025-01-27 08:50:50 +00:00
Šarūnas Nejus
55b7824948
Replace custom unescape implementation by html.unescape 2025-01-27 08:50:50 +00:00
Šarūnas Nejus
8a1ce27421
lyrics: Do not write item unless lyrics have changed 2025-01-27 08:50:50 +00:00
Šarūnas Nejus
8bdc2c6cf0
lyrics: Add symbols for better visual feedback in the logs 2025-01-27 08:50:50 +00:00
Šarūnas Nejus
f94d2767f9
Use a single slug implementation
Tidy up 'Google.is_page_candidate' method and remove 'Google.sluggify'
method which was a duplicate of 'slug'.

Since 'GeniusFetchTest' only tested whether the artist name is cleaned
up (the rest of the functionality is patched), remove it and move its
test cases to the 'test_slug' test.
2025-01-27 08:50:50 +00:00
Šarūnas Nejus
dd9f178fff
Do not try to strip cruft from the parsed lyrics text.
Having removed it I fuond that only the Genius lyrics changed: it had en
extra new line. Thus I defined a function 'collapse_newlines' which now
gets called for the Genius lyrics.
2025-01-27 08:50:50 +00:00
Šarūnas Nejus
7c2fb31136
Leave a single chef in the kitchen 2025-01-27 08:50:50 +00:00
Šarūnas Nejus
cb29605bfd
Include class name in the log messages 2025-01-27 08:50:50 +00:00
Šarūnas Nejus
283c513c72
Centralise request error handling 2025-01-27 08:50:49 +00:00
Šarūnas Nejus
06eac79c0d
Centralize requests setup with requests.Session
Improve requests performance with requests.Session which uses connection
pooling for repeated requests to the same host.

Additionally, this centralizes request configuration, making sure that
we use the same timeout and provide beets user agent for all requests.
2025-01-27 08:50:49 +00:00
Šarūnas Nejus
c40db1034a
Make lyrics plugin documentation slightly more clear 2025-01-27 08:50:49 +00:00
Šarūnas Nejus
2ff57505d8
Apply dist_thresh to Genius and Google backends
This commit introduces a distance threshold mechanism for the Genius and
Google backends.

- Create a new `SearchBackend` base class with a method `check_match`
  that performs checking.
- Start using undocumented `dist_thresh` configuration option for good,
  and mention it in the docs. This controls the maximum allowable
  distance for matching artist and title names.

These changes aim to improve the accuracy of lyrics matching, especially
when there are slight variations in artist or title names, see #4791.
2025-01-27 08:50:48 +00:00
J0J0 Todos
9d4653f92f Final lastgenre docstring nitpicks
and a tiny docs fix.
2025-01-23 09:04:06 +01:00
J0J0 Todos
2708be257d Final nitpicks on lastgenre --help wording 2025-01-23 08:32:10 +01:00
J0J0 Todos
2f40b315f4 Add lastgenre --no-force, reword help 2025-01-22 18:39:36 +01:00
J0J0 Todos
24a3394b97 Fix keep-none option, reword help
and a tiny hint along the way: clarify that -a is implicit.
2025-01-22 18:39:31 +01:00
J0J0 Todos
f695d463e2 Main variables init in _get_genre() 2025-01-22 18:14:42 +01:00
J0J0 Todos
1b05a1295d Revert "Further clarify lastgenre log-labels"
This reverts commit 8da98e52ee.

since we applied a PR suggestions where this is done in one place.
2025-01-22 18:11:18 +01:00
J0J0 Todos
86e1bd47a4 Single place for whitelist/any log-label suffix
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2025-01-22 18:07:43 +01:00
J0J0 Todos
87d9f57e24 In _get_existing_genres ensure empty string ignore 2025-01-22 18:07:43 +01:00
J0J0 Todos
92e84a2b45 Make sure existing genres are ALWAYS looked at
first thing. Only if genres are existing and force is disabled we return
early!
2025-01-21 18:06:11 +01:00
J0J0 Todos
6530d76319 Revert "Simplify _get_existing_genres()"
This reverts commit 7ff06df17c.

This was here for a reason: Ab empty string genre should als become an
empty list!
2025-01-21 17:44:17 +01:00
J0J0 Todos
4f0837c724 Revert "Include lower-casing in _get_existing already"
This reverts commit d5cf376a51.
2025-01-21 17:21:03 +01:00
J0J0 Todos
44901873f7 Clarify log-label: keep any, no-force 2025-01-21 17:04:03 +01:00
J0J0 Todos
d5cf376a51 Include lower-casing in _get_existing already
since we don't use it for early-returning no-force-existing genres
anymore.
2025-01-21 17:04:03 +01:00
J0J0 Todos
8da98e52ee Further clarify lastgenre log-labels
state if whitelist was applied or any genre is accepted.
2025-01-21 17:04:03 +01:00
J0J0 Todos
6e6a0ad9a9 Return empty tuple instead of disabling type issue 2025-01-21 17:04:03 +01:00
J0J0 Todos
c9187b40bd Don't uselessly split/join early returned genres 2025-01-21 17:04:03 +01:00
J0J0 Todos
169ec20a2f Remove unused _polish_existing_genres 2025-01-21 17:04:03 +01:00
J0J0 Todos
3cc2a5e2c6 Fix Tuple with tuple in sources property 2025-01-21 17:04:03 +01:00
J0J0 Todos
5d94eb3e13 Fix _get_genre docstring 2025-01-21 17:04:03 +01:00
J0J0 Todos
7ff06df17c Simplify _get_existing_genres()
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2025-01-21 17:04:03 +01:00
J0J0 Todos
c3f0abd61c Fix docstring _resolve_genres
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2025-01-21 17:04:03 +01:00
J0J0 Todos
bb3f9c53c2 lastgenre new defaults, remove config sanity check 2025-01-21 17:04:03 +01:00
J0J0 Todos
ca5e471f05 Refactor again _last_lookup 2025-01-21 17:04:03 +01:00
J0J0 Todos
ec10507fab Return as-is if no-force 2025-01-21 17:04:03 +01:00
J0J0 Todos
6e3f5b3127 Fix type hints, Refactor existing genres method
- Rename method _dedup_genre, since it's only used for
  finalizing/polishing existing genres.
- Return separator-delimited string already.
- Decide on not passing "separator" to methods, it's a config
  setting available throughout the plugin. Assign to variable where
  useful for readability though.
- In the force branch, remove re-assigning keep_genres to empty list.
- Fix a test. Existing genres are "polished" now, which means:
  configured title_case is applied.
- Fix/add type hints on all touched and new methods
2025-01-21 17:04:03 +01:00
J0J0 Todos
ed68bc019b Refactor _get_genre, simplify _combine_genre 2025-01-21 17:04:03 +01:00
J0J0 Todos
4580757c8e Simplify _last_lookup()
f-string, list comprehension, remove redundant vars.
2025-01-21 17:04:03 +01:00
J0J0 Todos
f16e671ff6 Simplify _get_genre keep_existing conditional
- If the keep_existing option is set, just remember everything for now.
- Dedup happening later on via _combine... _resolve_genres...
- Even knowing if whitelist or not is not important at this point.
2025-01-21 17:04:03 +01:00
J0J0 Todos
f698f21a28 Ensure _resolve returns list, add type hint
Prevents potential type erros when handing over to
_to_delimited_genre_string.
2025-01-21 17:04:03 +01:00
J0J0 Todos
d358a24ed9 Remove redundant unique_list call in _combine
and clarify in _resolve_genre docstring.
2025-01-21 17:04:02 +01:00
J0J0 Todos
2a80a10aa8 Use util.unique_list in fav of deduplicate 2025-01-21 17:04:02 +01:00
J0J0 Todos
79b5379dce Refactor and rename _is_valid() helper 2025-01-21 17:04:02 +01:00
J0J0 Todos
e1fe6fd3d0 Prevent album genre inherit only when source:track
is configured.
2025-01-21 17:04:02 +01:00
J0J0 Todos
bd0c02437a Apply temp logging leftover review suggestions
Useless variables that only were introduced for temporary debug logging
while refactoring earlier. Get rid of them.

Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2025-01-21 17:04:02 +01:00
J0J0 Todos
b476560d76 Integrate _format_tag in _to_delimited_...
optimize by checking for config once and simplify tags list slicing.
Remove _format_tags method.

Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2025-01-21 17:04:02 +01:00
J0J0 Todos
593f5460b6 Apply type hint suggestions from review
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2025-01-21 17:04:02 +01:00
J0J0 Todos
6b41743b91 Polish 'fetched last.fm tags' debug message
The best place to log what we actually fetched from last.fm seems to be
here in _combine_and_label_genres. Leave out the existing genres we also
receive in this function - less is more.
2025-01-21 17:04:02 +01:00
J0J0 Todos
8d43517a71 Remove all lastgenre temporary debug logging 2025-01-21 17:04:02 +01:00
J0J0 Todos
847b7260b4 Fix most popular track genre fetching (VA albums) 2025-01-21 17:04:02 +01:00
J0J0 Todos
1aca3989d7 Fix previous temp log messages 2025-01-21 17:04:02 +01:00
J0J0 Todos
da98d37632 Temporary debug messages in _sort_by_depth 2025-01-21 17:04:02 +01:00
J0J0 Todos
a717998587 Add a temporary log around whitelist setup 2025-01-21 17:04:02 +01:00
J0J0 Todos
37fc1b6b7f Final lastgenre plugin linting 2025-01-21 17:04:02 +01:00
J0J0 Todos
13ae699230 Fix _dedup_genre, ensure lower case
otherwise deduplicate() can't handle it.
2025-01-21 17:04:02 +01:00
J0J0 Todos
569ba73016 Refactor again _combine_and_label_genres 2025-01-21 17:04:02 +01:00
J0J0 Todos
3d036473e7 lastgenre _is_allowed detailed logging
- Add detailed debug logging to learn when and why things go wrong here.
- Shorten docstring
2025-01-21 17:04:02 +01:00
J0J0 Todos
9f5b4badb2 Handle genres as list, count/format/str helper
- Return fetched genres as a list from _resolve_genres().
- Format, limit to count and join to delimited string in helper
  function.
- Fix docstring.
- Leave a couple of temporary debug messages.
- Fix original genre fallback - just keep as-is.
2025-01-21 17:04:02 +01:00
J0J0 Todos
5e6d9170d7 Fix lastgenre "count" issue
When original genres were kept (keep_existing option), the final genre
count was "off". The reason was that reducing genres to that count is
handled in _resolve_genre which wasn't run.

- This fixes it by ensuring a run of _resolve_genre in
  _combine_and_label_genres.
- There is a small caveat though: New genres have been run through
  _resolve_genres already. When they are combined with the old ones,
  they run through it again. Let's take this into account for now and
  hope performance doesn't suffer too much.
2025-01-21 17:04:02 +01:00
J0J0 Todos
d0abc0d830 Rename lastgenre option, refactor, new default
- Refactor and simplify logic of _get_genre()
- Add a config validation function.
- New default force: yes, keep_existing: yes (closest to original
  behaviour)
2025-01-21 17:04:02 +01:00
J0J0 Todos
4c7d0c98cf Clarify lastgenre _is_allowed docstring 2025-01-21 17:04:02 +01:00
J0J0 Todos
90c48ea8cf Temporary lastgenre debug messages for @arsaboo 2025-01-21 17:04:02 +01:00
J0J0 Todos
b0e0f1b048 Fallback to next stage when fetch_ returns None
This was the original behaviour and broke when _combine_and_label helper
was introduced.
2025-01-21 17:04:02 +01:00
J0J0 Todos
462a7a524a _combine_and_label return None not empty str 2025-01-21 17:04:02 +01:00
J0J0 Todos
6866fce364 Fix default for _dedup_genre whitelist arg
when not stated otherwise whitelist_only must be disabled, we assume it
that way in _get_genre calls.
2025-01-21 17:04:02 +01:00
J0J0 Todos
cbc33e78fc lastgenre: Add comments over groups of methods
trying to get a little order in the chaos. Maybe reordering and/or
moving out of the main plugin logic would be a better idea for some
methods but don't put much more refactoring into this PR to keep it
readable.
2025-01-21 17:04:02 +01:00
J0J0 Todos
11f7a98917 Refactor keep/new genres combination
- Handle genre combination logic in a well documented helper function
  that also include type hints.
- Throughout the _get_genre function rename the result variable to
  new_genres to make it clearly descriptive.
- Rewrite thze _get_genre function's docstring.
2025-01-21 17:04:02 +01:00
J0J0 Todos
d935ec869c Implement --force and --keep-allowed behaviours
- Retrieving, filtering and deduplicating present genres of Items/Albums
  via separate methods.
- Implement all four cases of behaviour as described in PR#4982
- Issues:
  - There is quite some unnecessary spliting of genres from strings into
    lists and the other way round happening throughout the plugin.
  - In the case where existing genres get "augmented" with last.fm
    genres, we might end up with _more_ genres than the configured
    limit.
2025-01-21 17:04:02 +01:00
J0J0 Todos
318c02099a Add lastgenre keep_allowed options (-k/-K)
- Default to False.
- During PR#4982 discussions we came to the conclusion that the
  following behaviour would be a good new default choice:
  - Keep whitelisted existing genres
  - Only Fetch last.fm genres for empty tags.
  - To get this we also have to change the default of the force
    option!!!
  - Resulting in "force: no" and "keep_allowed: yes"; see Case 4 in
    PR#4982 description
- Options are not put to use yet, just defined and defaults set!
2025-01-21 17:04:02 +01:00
J0J0 Todos
4ea8650799 Use provided deduplicate function for keep_allowed
generation, instead of a simple set(). This way we keep the original
order of genres.
2025-01-21 17:04:02 +01:00
J0J0 Todos
fe466f4bb3 Use separator as configured instead of hardcoding
in lastgenre plugin.
2025-01-21 17:04:02 +01:00
J0J0 Todos
517c037c25 Refactor lastgenre keep_allowed to list comprehension 2025-01-21 17:04:02 +01:00
J0J0 Todos
7d6a4046ce Handle dups of existing genres in lastgenre plugin
When handling existing comma-separated genres in the _get_genre method
of the plugin, make sure duplicate genres are removed.
2025-01-21 17:04:02 +01:00
J0J0 Todos
6ab6ae2051 Fix track-level genre handling in lastgenre plugin
When `lastgenre.source: track` is configured,

- `lastgenre -a` _should not_ fall back to the album level genre (by
  making use of the with_album=False kwarg of the Libary's get method).
- `lastgenre -a`, when finally storing the genres of _an album_, should
  _not_ also write the tracks genres (by making use of the inherit=False
  kwarg of the Album's store method.
2025-01-21 17:04:02 +01:00
J0J0 Todos
4ff8c34ed1 Quickfix lastgenre always overwriting multi-genre 2025-01-21 17:04:02 +01:00
Šarūnas Nejus
bb5f3e0593
lyrics: sort lrclib lyrics by synced field and query search first
I found that the `/get` endpoint often returns incorrect or unsynced
lyrics, while results returned by the `/search` more accurate options.

Thus I reversed the change in the previous commit to prioritize
searching first.
2025-01-20 13:14:37 +00:00
Šarūnas Nejus
33aafdd50b
Remove trailing spaces in synced lyrics lines without text 2025-01-19 18:39:56 +00:00
Šarūnas Nejus
618c3a21a6
Try to GET LRCLib lyrics before searching 2025-01-19 18:39:54 +00:00
Šarūnas Nejus
2fb72c65a5
lyrics/LRCLib: handle instrumental lyrics 2025-01-19 15:19:44 +00:00
Šarūnas Nejus
30379bca38
Update lyrics.sources configuration to prioritize lrclib 2025-01-19 15:19:44 +00:00
Šarūnas Nejus
a398fbe62d
LRCLib: Improve exception handling 2025-01-19 15:19:44 +00:00
Šarūnas Nejus
8d4a569291
Fix fetching lyrics from lrclib
Adjust the base URL to perform a '/search' instead of attempting to
'/get' specific lyrics where we're unlikely to find lyrics for the
specific combination of album, artist, track names and the duration (see
https://lrclib.net/docs).

Since we receive an array of matching lyrics candidates, rank them by
their duration similarity to the item's duration, and whether they
contain synced lyrics.
2025-01-19 15:19:41 +00:00
Šarūnas Nejus
c250bfa724
Google: test the entire fetch method 2025-01-19 01:48:04 +00:00
Šarūnas Nejus
334bbde826
Make album, duration required for LyricsPlugin.fetch
Since at least one Backend requires album` and `duration` arguments
(`LRCLib`), the caller (`LyricsPlugin.fetch_item_lyrics`) must always
provide them.

Since they need to provided, we need to enforce this by defining them as
positional arguments.

Why is this important? I found that integrated `LRCLib` tests have been
passing, but they called `LRCLib.fetch` with values for `artist` and
`title` fields only, while the actual functionality *always* provides
values for `album` and `duration` fields too.

When I adjusted the test to provide values for the missing fields,
I found that it failed. This makes sense: Lib `album` and `duration`
filters are strict on LRCLib, so I was not surprised the lyrics could
not be found.

Thus I adjusted `LRCLib` backend implementation to only filter by each
of these fields when their values are truthy.
2025-01-19 01:48:04 +00:00
Šarūnas Nejus
0a12d07a94
Do not attempt to fetch lyrics with empty data
Modified `search_pairs` function in `lyrics.py` to:

* Firstly strip each of `artist`, `artist_sort` and `title` fields
* Only generate alternatives if both `artist` and `title` are not empty
* Ensure that `artist_sort` is not empty and not equal to artist (ignoring
  case) before appending it to the artists

Extended tests to cover the changes.
2025-01-19 01:48:04 +00:00
Šarūnas Nejus
3b73a26002
Address failing google sources tests
Two google sources failed to return the expected output. I looked into
each case why parsing failed:

- lyrics on musica.com contain <aside> Google Ads
- each lyrics line on lacoccinelle.net is wrapped within alternating
  <em> and <strong> tags

Thus remove these tags as part of the HTML cleanup logic.
2025-01-19 01:32:17 +00:00
Šarūnas Nejus
a8ad7df064
Use Item.field_query for queries that receive user input 2025-01-19 01:09:11 +00:00
Šarūnas Nejus
4650f6513b
Add Item.any_writable_media_field_query method for BPD search 2025-01-19 01:09:11 +00:00
J0J0 Todos
0c10635ff7 Another round of lastgenre logging nitpicks
- Printing out album/item in default format could lead to unreadable
  clutter depending on the user's configured formats.
- The album's name and the individual tracks' title should be just
  sufficient to provide context as well readability.
- Log like this while importing as well as in standalone runs.
2025-01-07 01:54:17 +01:00
J0J0 Todos
9d09d6f317 Fix lastgenre source:track handling during imports 2025-01-07 01:54:17 +01:00
J0J0 Todos
18e76f08c7 Prevent album genre inherit only when source:track
is configured.
2025-01-07 01:54:17 +01:00
J0J0 Todos
9ec2a8146f Streamline lastgenre singleton log with album log
It was rather confusing that the lastgenre plugin, when handling
singletons, sometimes showed that it applied genres from last.fm and
sometimes didn't (it did only in debug log). This streamlines the
behaviour:

- Change debug to info log.
- Streamline wording.
- Display details about the track.
2025-01-07 01:54:17 +01:00
J0J0 Todos
d4ada3ce43 Fix track-level genre handling in lastgenre plugin
When `lastgenre.source: track` is configured,

- `lastgenre -a` _should not_ fall back to the album level genre (by
  making use of the with_album=False kwarg of the Libary's get method).
- `lastgenre -a`, when finally storing the genres of _an album_, should
  _not_ also write the tracks genres (by making use of the inherit=False
  kwarg of the Album's store method.
2025-01-07 01:53:58 +01:00
Šarūnas Nejus
5c81f94cf7
Move imports required for typing under the TYPE_CHECKING block 2024-12-10 06:10:04 +00:00
Šarūnas Nejus
161b0522bb
Update deprecated imports 2024-12-10 06:10:04 +00:00
Šarūnas Nejus
7ef1b61070
Replace Union types by PEP604 pipe character 2024-12-10 06:10:04 +00:00
Šarūnas Nejus
51f9dd229e
Use PEP585 lowercase collections typing annotations 2024-12-10 06:10:03 +00:00
Edgars Supe
09360259cc lyrics: Fallback to plain lyrics if synced not available 2024-12-07 19:08:37 +02:00
Šarūnas Nejus
65e935bee5
Perform a regex substitution in the substitute plugin (#5357)
This utilises regex substitution in the substitute plugin. The previous
approach only used regex to match the pattern, then replaced it with a
static string. This change allows more complex substitutions, where the
output depends on the input.

### Example use case
Say we want to keep only the first artist of a multi-artist credit, as
in the following list:
```
Neil Young & Crazy Horse -> Neil Young
Michael Hurley, The Holy Modal Rounders, Jeffrey Frederick & The Clamtones -> Michael Hurley
James Yorkston and the Athletes -> James Yorkston
````
This would previously have required three separate rules, one for each
resulting artist. By using a regex substitution, we can get the desired
behaviour in a single rule:
```yaml
substitute:
  ^(.*?)(,| &| and).*: \1
```
(Capture the text until the first `,` ` &` or ` and`, then use that
capture group as the output)

### Notes
I've kept the previous behaviour of only applying the first matching
rule, but I'm not 100% sure it's the ideal approach.
I can imagine both cases where you want to apply several rules in
sequence and cases where you want to stop after the first match.
2024-11-22 05:02:50 +00:00
Alok Saboo
b9c6ee208e lint error 2024-11-10 20:27:41 -05:00
Alok Saboo
35c6e13255 Update line length 2024-11-10 20:25:28 -05:00
Alok Saboo
3586669dd5 Merge branch 'lb_error' of https://github.com/arsaboo/beets into lb_error 2024-11-10 20:24:36 -05:00
Alok Saboo
24115167d3 Merge remote-tracking branch 'upstream/master' into lb_error 2024-11-10 20:23:34 -05:00
Alok Saboo
c1b4b58e65
Update beetsplug/listenbrainz.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2024-11-10 20:23:11 -05:00
Šarūnas Nejus
69dbfd9868
Fix lints
These seem to have managed to escape the CI checks since the previously
merged PR was based on master commit which did not include the checks.
2024-10-30 12:13:30 +00:00
Joseph Bushell
4b78abd939 create seperate in_no_convert function, update tests 2024-10-26 19:09:56 +01:00
Joseph Bushell
d1c88bbd25 consider value of no_convert as one query rather than splitting 2024-10-26 18:17:00 +01:00
Alok Saboo
93aa569e3f Improve ListenBrainz error handling and simplify playlist handling 2024-10-25 10:06:54 -04:00
Nicholas Boyd Isacsson
8e0558b804 Apply substitute rules in sequence 2024-10-16 16:36:36 +02:00
Nicholas Boyd Isacsson
ffdc3f73ab Revert configuration format changes 2024-10-14 22:54:38 +02:00
Nicholas Boyd Isacsson
195644fc46 Refactor according to review comments 2024-10-12 15:34:05 +02:00
Šarūnas Nejus
d3955bac65
Update Tekstowo backend to fetch lyrics directly
- Refactored Tekstowo backend to fetch lyrics directly from song pages.
- Added `encode` method to convert artist and title to their URL format,
  where non-alphanumeric characters are replaced with underscores.
- Removed the now redundant search functionality and associated tests.
- Simplified `extract_lyrics` method to directly parse lyrics without
  any checks.
2024-10-12 02:14:18 +01:00
Šarūnas Nejus
9d2b34d457
Create DirectBackend interface for MusiXmatch and Tekstowo
And replace some of the encoding logic by unidecode.
2024-10-12 01:29:55 +01:00
Nicholas Boyd Isacsson
9bc586d7ea Replace Py3.10+ pattern matching with isinstance 2024-10-05 15:51:41 +02:00
Nicholas Boyd Isacsson
913d51af5c Preserve rule order in substitute configuration 2024-10-05 15:51:41 +02:00
Nicholas Boyd Isacsson
9680d8f3f5 Rename variables 2024-10-05 15:51:41 +02:00
Nicholas Boyd Isacsson
7b5d818603 Use a regex substitution in substitute 2024-10-05 15:51:41 +02:00
Nicholas Boyd Isacsson
e613a57ca4 Use case insensitive regex for substitute 2024-10-05 15:51:41 +02:00
Karl Besser
ed627c031c Hardcode for_artist keyword in ftintitle plugin
Remove the optional `for_artist` keyword in the
`ftintitle.contains_feat` function and hardcode it to False, since it is
always used like this in the ftintitle plugin.
2024-09-30 10:24:09 -05:00
Karl-Ludwig Besser
ab86b2d1e8
Merge branch 'beetbox:master' into fix_false_positive_ftintitle 2024-09-30 10:17:19 -05:00
Andrew Rogl
04ee04150a
Reworked #4709 after latest release (#5447)
Fixes #4709 SQL use of Double Quoted Strings.
2024-09-30 10:28:15 +01:00
Karl Besser
c66eb10445 Fix false positives in "feat. X" detection in ftintitle
The old version of the `ftintitle.contains_feat` function could lead to
false positives by matching words like "and" and "with" in the title,
even if there was no "feat. X" part.
With this commit, the `for_artist` keyword is explicitly passed to the
`plugins.feat_tokens` function to disable these matches when matching a
title (and not an artist).
2024-09-26 17:20:12 -04:00
Šarūnas Nejus
5f78d1b82b
Remove some lint exclusions and fix the issues
* Replace `noqa` comments in `assert...` method definitions with
  a configuration option to ignore these names.
* Use the `__all__` variable to specify importable items from the
  module, replacing `*` imports and `noqa` comments for unused imports.
* Address issues with poorly named variables and methods by renaming
  them appropriately.
2024-09-21 11:59:19 +01:00
Šarūnas Nejus
f36bc497c8
Fix lint issues
- Fix imports
- Fix pytest issues
- Do not assign lambda as variable
- Use isinstance instead of type to check type
- Rename ambiguously named variables
- Name custom errors with Error suffix
2024-09-21 11:59:18 +01:00
Šarūnas Nejus
85a17ee503
Reformat the codebase 2024-09-21 11:57:48 +01:00
Karl Besser
218cda0401 Fix missing keep_in_artist option in ftintitle
The newly introduced option to keep the feat. artist in the artist field
when importing with the ftintitle plugin was not passed in one function.
2024-09-20 20:47:10 -04:00
Konstantin
796c9addfb
Fixes TypeError in the Discogs plugin (#5415)
Handles the case when discogs return `None` for format descriptions.

---------

Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2024-09-19 21:27:16 +01:00
Šarūnas Nejus
c13f0f2f6e
Deezer: Improve requests error handling (#5421)
This PR adds gracefully handling requests error in teh Deezer plugin.
2024-09-17 18:52:42 +01:00
Alok Saboo
7f74d3db4c
Update beetsplug/deezer.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2024-09-17 13:23:11 -04:00
Šarūnas Nejus
5785522170
fetchart: defer file removal config option evaluation (#5244)
Defer the evaluation of the source file removal options (`import.delete`
and `import.move`) to the point where the fetchart plugin is actually
called instead of only evaluating those configuration options on plugin
initialization.
This is to allow other plugins (such as the
[ytimport](https://github.com/mgoltzsche/beets-ytimport/blob/v1.8.1/beetsplug/ytimport/__init__.py#L194)
plugin) to invoke the import directly (within the same python process;
implicitly invoking the fetchart plugin) with temporarily overwritten
configuration options.

Addresses
https://github.com/beetbox/beets/issues/5167#issuecomment-2106465172
2024-09-16 11:44:46 +01:00
Alok Saboo
afe43dc360 Fix lint errors 2024-09-15 11:36:16 -04:00
Alok Saboo
f57dc66444 Deezer: Improve requests error handling
## Description

This PR adds gracefully handling requests error in teh Deezer plugin. Right now, it errors out when it receives error:

```bash
Traceback (most recent call last):
  File "/home/arsaboo/.local/bin/beet", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/ui/__init__.py", line 1865, in main
    _raw_main(args)
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/ui/__init__.py", line 1852, in _raw_main
    subcommand.func(lib, suboptions, subargs)
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/ui/commands.py", line 1395, in import_func
    import_files(lib, paths, query)
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/ui/commands.py", line 1326, in import_files
    session.run()
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/importer.py", line 360, in run
    pl.run_parallel(QUEUE_SIZE)
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/util/pipeline.py", line 447, in run_parallel
    raise exc_info[1].with_traceback(exc_info[2])
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/util/pipeline.py", line 312, in run
    out = self.coro.send(msg)
          ^^^^^^^^^^^^^^^^^^^
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/util/pipeline.py", line 195, in coro
    func(*(args + (task,)))
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/importer.py", line 1497, in lookup_candidates
    task.lookup_candidates()
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/importer.py", line 688, in lookup_candidates
    artist, album, prop = autotag.tag_album(
                          ^^^^^^^^^^^^^^^^^^
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/autotag/match.py", line 548, in tag_album
    for matched_candidate in hooks.album_candidates(
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/plugins.py", line 593, in decorated
    for v in generator(*args, **kwargs):
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/autotag/hooks.py", line 759, in album_candidates
    yield from plugins.candidates(items, artist, album, va_likely, extra_tags)
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/plugins.py", line 390, in candidates
    yield from plugin.candidates(
               ^^^^^^^^^^^^^^^^^^
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beets/plugins.py", line 772, in candidates
    results = self._search_api(query_type="album", filters=query_filters)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/arsaboo/.local/lib/python3.12/site-packages/beetsplug/deezer.py", line 282, in _search_api
    response.raise_for_status()
  File "/home/arsaboo/.local/lib/python3.12/site-packages/requests/models.py", line 1024, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://api.deezer.com/search/album?q=album%3A%22Y+Hate+%3F%22+artist%3A%22Parmish+Verma%22
```

## To Do

- [ ] Documentation. (If you've added a new command-line flag, for example, find the appropriate page under `docs/` to describe it.)
- [x] Changelog. (Add an entry to `docs/changelog.rst` to the bottom of one of the lists near the top of the document.)
- [ ] Tests. (Very much encouraged but not strictly required.)
2024-09-15 11:34:29 -04:00
Max Goltzsche
00add272ce
fetchart: apply review remarks
simplifying config access

Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2024-09-13 23:09:49 +02:00
Karl-Ludwig Besser
39acba5200
Merge branch 'master' into ftintitle_keep_artist 2024-09-05 12:32:20 +02:00
Karl Besser
15d652dbae Update log message for keeping artist in ftintitle
Update the log message when the artist is kept unchanged due to setting
the keep_in_artist option to true.
2024-09-05 12:28:51 +02:00
Šarūnas Nejus
591e04a894
Add Item.filepath property to simplify path handling
Additionally, fix DefaultTemplateFunctions._func_names definition.
2024-08-19 22:44:17 +01:00
Šarūnas Nejus
8f02020db4
autobpm: handle errors in beat.beat_track as well 2024-08-19 22:44:17 +01:00
Šarūnas Nejus
3c51128d9b
autobpm: do not call get on boolean config options 2024-08-19 22:44:17 +01:00
Šarūnas Nejus
ee2f114342
autobpm: add types 2024-08-19 22:44:17 +01:00
Šarūnas Nejus
e3776f1910
autobpm: Do not import 'beat' subpackage immediately to improve startup time 2024-08-19 22:44:16 +01:00
Šarūnas Nejus
5b5c564a69
autobpm: Add a new beat_track_kwargs configuration option to autobpm 2024-08-19 22:44:16 +01:00
Šarūnas Nejus
49cae5ca23
autobpm: Fix the issue with tempo being a numpy array 2024-08-19 22:44:16 +01:00
Šarūnas Nejus
1163645604
Make sure tests do not leave any temp files behind (#5345)
Fixes #5229, is part of #5361 and relates to #5285.

I have to admit thsi was a fairly tough task - I initially assumed that
the problem lies
with how the tests are setup, and that we're probably missing some
`teardown_beets` calls
here and there.

Unfortunately, it was not so simple. I came across several issues that
gave rise to
leftover temporary files:

1. `fetchart`, `artresizer` and `play` handling of temporary files.
These plugins created
isolated temporary files outside of the directories that tests clean up.
You will find
I added a couple of functions (namely `get_module_tempdir`) that force
these plugins to
create files in directories determined by their module names. This way
we can clean up
   after them using the new `CleanupModulesMixin`.

2. Tests that ran temporary directories setup twice, running
`_common.TestCase.setUp` and
`test.helper.TestHelper.setup_beets`. Both of these ran `self.temp_dir =
mkdtemp()`,
therefore the directories created by the initial setup persisted since
those have been
overridden and thus unreachable in the teardown. Here, I removed the
`setUp` calls, see

   - `test/plugins/test_embedart.py`
   - `test/test_importer.py`
   - and `test/test_plugins.py` where `setup_beets` was called twice

3. `test/test_config_command.py` attempted to manage the temporary
directory by itself,
where I found that `tearDown` failed to remove the directory for four
tests. Could not
figure out the cause, and found that delegating this task to
`TestHelper` fixed the
   issue.

4. Mediafile fixture removal depended on calling
`remove_mediafile_fixtures` method, which
`test/plugins/test_zero.py` failed to do. I made the fixtures to be
created within the
same `temp_dir` directory that gets removed in the teardown, so now they
are taken care
   of automatically.

In summary, see the test modules that left files behind:

```
Temp files created by test/__init__.py
Temp files created by test/plugins/__init__.py
Temp files created by test/plugins/lyrics_download_samples.py
Temp files created by test/plugins/test_acousticbrainz.py
Temp files created by test/plugins/test_advancedrewrite.py
Temp files created by test/plugins/test_albumtypes.py
Temp files created by test/plugins/test_art.py
	/tmp/tmp11nicahe.jpg
	/tmp/tmp1bjmodum.png
	/tmp/tmped7nhls4.jpg
	/tmp/tmpflnzr9wz.jpg
	/tmp/tmpjngkauqs.png
	/tmp/tmpkzy9mn6t.jpg
	/tmp/tmpph_wmuea.jpg
	/tmp/tmps6gk58i_.jpg
	/tmp/tmpz2eji_o4.jpg
Temp files created by test/plugins/test_aura.py
Temp files created by test/plugins/test_bareasc.py
	/tmp/tmphl3kzhug
	/tmp/tmpnh2q6v02
	/tmp/tmpppw5qrhz
Temp files created by test/plugins/test_beatport.py
Temp files created by test/plugins/test_bucket.py
Temp files created by test/plugins/test_convert.py
Temp files created by test/plugins/test_discogs.py
Temp files created by test/plugins/test_edit.py
Temp files created by test/plugins/test_embedart.py
	/tmp/tmp1ayvqzhx
	/tmp/tmp58k6mdfx.jpg
	/tmp/tmp64c2lqiv
	/tmp/tmp6nar4kr5
	/tmp/tmp6u0d5dex
	/tmp/tmpacoq7w_f
	/tmp/tmpajnr_sxr
	/tmp/tmpasj16beh
	/tmp/tmpboyaixb5
	/tmp/tmpcrmcyt5r
	/tmp/tmpdomje5g3
	/tmp/tmplu3o6t6g
	/tmp/tmpns_xvkns
	/tmp/tmpo87o1h6o.jpg
	/tmp/tmpqem39h_j
	/tmp/tmprlzm18pb
	/tmp/tmpt22v4u6x
	/tmp/tmptp3rxdgv
Temp files created by test/plugins/test_embyupdate.py
Temp files created by test/plugins/test_export.py
Temp files created by test/plugins/test_fetchart.py
Temp files created by test/plugins/test_filefilter.py
Temp files created by test/plugins/test_ftintitle.py
Temp files created by test/plugins/test_hook.py
Temp files created by test/plugins/test_ihate.py
Temp files created by test/plugins/test_importadded.py
Temp files created by test/plugins/test_importfeeds.py
Temp files created by test/plugins/test_info.py
Temp files created by test/plugins/test_ipfs.py
Temp files created by test/plugins/test_keyfinder.py
Temp files created by test/plugins/test_lastgenre.py
Temp files created by test/plugins/test_limit.py
Temp files created by test/plugins/test_lyrics.py
Temp files created by test/plugins/test_mbsubmit.py
Temp files created by test/plugins/test_mbsync.py
Temp files created by test/plugins/test_mpdstats.py
Temp files created by test/plugins/test_parentwork.py
Temp files created by test/plugins/test_permissions.py
Temp files created by test/plugins/test_player.py
Temp files created by test/plugins/test_playlist.py
Temp files created by test/plugins/test_play.py
	/tmp/tmp6ohknmve.m3u
	/tmp/tmp8rw2z_j4.m3u
	/tmp/tmp9vi27ypx.m3u
	/tmp/tmpa_s66jh8.m3u
	/tmp/tmpb7h3cn3n.m3u
	/tmp/tmpexbmqvry.m3u
	/tmp/tmpinbqrt80.m3u
	/tmp/tmpql02hax5.m3u
	/tmp/tmpvbdzprsf.m3u
	/tmp/tmpzipim36x.m3u
Temp files created by test/plugins/test_plexupdate.py
Temp files created by test/plugins/test_plugin_mediafield.py
Temp files created by test/plugins/test_random.py
Temp files created by test/plugins/test_replaygain.py
Temp files created by test/plugins/test_smartplaylist.py
Temp files created by test/plugins/test_spotify.py
Temp files created by test/plugins/test_subsonicupdate.py
Temp files created by test/plugins/test_the.py
Temp files created by test/plugins/test_thumbnails.py
Temp files created by test/plugins/test_types_plugin.py
Temp files created by test/plugins/test_web.py
Temp files created by test/plugins/test_zero.py
	/tmp/tmp3ub9xmzy
Temp files created by test/rsrc/beetsplug/test.py
Temp files created by test/rsrc/convert_stub.py
Temp files created by test/testall.py
Temp files created by test/test_art_resize.py
	/tmp/tmp3p7p60ih.jpg
	/tmp/tmp8exclgit.jpg
	/tmp/tmpkrrjsitl.jpg
	/tmp/tmpw6n8ee8e.jpg
	/tmp/tmpygws_0aw.jpg
Temp files created by test/test_autotag.py
Temp files created by test/test_config_command.py
	/tmp/tmp333f0r2j
	/tmp/tmphr356z5r
	/tmp/tmporp4rag2
	/tmp/tmpy7sjqdsw
Temp files created by test/test_datequery.py
Temp files created by test/test_dbcore.py
Temp files created by test/test_files.py
Temp files created by test/test_hidden.py
Temp files created by test/test_importer.py
	/tmp/tmp0m363gfb
	/tmp/tmp2n3i13mc
	/tmp/tmpxk3v304s
Temp files created by test/test_library.py
Temp files created by test/test_logging.py
Temp files created by test/test_m3ufile.py
Temp files created by test/test_mb.py
Temp files created by test/test_metasync.py
Temp files created by test/test_pipeline.py
Temp files created by test/test_plugins.py
	/tmp/tmp6pxhx67u
	/tmp/tmpb8pqi9ui
	/tmp/tmpcx_658g7
	/tmp/tmp_giqb9jz
	/tmp/tmpgm9xk94_
	/tmp/tmpk60l6bt3
	/tmp/tmpqoj4la68
	/tmp/tmptcdu20rp
	/tmp/tmpvr7k5shn
	/tmp/tmpwnfnzs91
Temp files created by test/test_query.py
Temp files created by test/test_sort.py
Temp files created by test/test_template.py
Temp files created by test/test_ui_commands.py
	/tmp/tmpns2u94w6
Temp files created by test/test_ui_importer.py
Temp files created by test/test_ui_init.py
Temp files created by test/test_ui.py
Temp files created by test/test_util.py
Temp files created by test/test_vfs.py
```

And that's what we have right now:

```
Temp files created by test/__init__.py
Temp files created by test/plugins/__init__.py
Temp files created by test/plugins/lyrics_download_samples.py
Temp files created by test/plugins/test_acousticbrainz.py
Temp files created by test/plugins/test_advancedrewrite.py
Temp files created by test/plugins/test_albumtypes.py
Temp files created by test/plugins/test_art.py
Temp files created by test/plugins/test_aura.py
Temp files created by test/plugins/test_bareasc.py
Temp files created by test/plugins/test_beatport.py
Temp files created by test/plugins/test_bucket.py
Temp files created by test/plugins/test_convert.py
Temp files created by test/plugins/test_discogs.py
Temp files created by test/plugins/test_edit.py
Temp files created by test/plugins/test_embedart.py
Temp files created by test/plugins/test_embyupdate.py
Temp files created by test/plugins/test_export.py
Temp files created by test/plugins/test_fetchart.py
Temp files created by test/plugins/test_filefilter.py
Temp files created by test/plugins/test_ftintitle.py
Temp files created by test/plugins/test_hook.py
Temp files created by test/plugins/test_ihate.py
Temp files created by test/plugins/test_importadded.py
Temp files created by test/plugins/test_importfeeds.py
Temp files created by test/plugins/test_info.py
Temp files created by test/plugins/test_ipfs.py
Temp files created by test/plugins/test_keyfinder.py
Temp files created by test/plugins/test_lastgenre.py
Temp files created by test/plugins/test_limit.py
Temp files created by test/plugins/test_lyrics.py
Temp files created by test/plugins/test_mbsubmit.py
Temp files created by test/plugins/test_mbsync.py
Temp files created by test/plugins/test_mpdstats.py
Temp files created by test/plugins/test_parentwork.py
Temp files created by test/plugins/test_permissions.py
Temp files created by test/plugins/test_player.py
Temp files created by test/plugins/test_playlist.py
Temp files created by test/plugins/test_play.py
Temp files created by test/plugins/test_plexupdate.py
Temp files created by test/plugins/test_plugin_mediafield.py
Temp files created by test/plugins/test_random.py
Temp files created by test/plugins/test_replaygain.py
Temp files created by test/plugins/test_smartplaylist.py
Temp files created by test/plugins/test_spotify.py
Temp files created by test/plugins/test_subsonicupdate.py
Temp files created by test/plugins/test_the.py
Temp files created by test/plugins/test_thumbnails.py
Temp files created by test/plugins/test_types_plugin.py
Temp files created by test/plugins/test_web.py
Temp files created by test/plugins/test_zero.py
Temp files created by test/rsrc/beetsplug/test.py
Temp files created by test/rsrc/convert_stub.py
Temp files created by test/testall.py
Temp files created by test/test_art_resize.py
Temp files created by test/test_autotag.py
Temp files created by test/test_config_command.py
Temp files created by test/test_datequery.py
Temp files created by test/test_dbcore.py
Temp files created by test/test_files.py
Temp files created by test/test_hidden.py
Temp files created by test/test_importer.py
Temp files created by test/test_library.py
Temp files created by test/test_logging.py
Temp files created by test/test_m3ufile.py
Temp files created by test/test_mb.py
Temp files created by test/test_metasync.py
Temp files created by test/test_pipeline.py
Temp files created by test/test_plugins.py
Temp files created by test/test_query.py
Temp files created by test/test_sort.py
Temp files created by test/test_template.py
Temp files created by test/test_ui_commands.py
Temp files created by test/test_ui_importer.py
Temp files created by test/test_ui_init.py
Temp files created by test/test_ui.py
Temp files created by test/test_util.py
Temp files created by test/test_vfs.py
```

Note that the command which provides the output is now available through
`poe`.
2024-07-18 12:25:23 +01:00
Serene-Arc
79449b0851
Filter ending newlines 2024-07-11 15:15:40 +10:00
Karl Besser
c046dc3976 Add new keep_in_artist option for ftintitle plugin
The new keep_in_artist option allows keeping the feat. part in the
artist metadata field while still changing the title.
2024-07-09 23:49:20 -04:00
HomerHaddock
50bfe6a179 Fix bug #4815 2024-07-06 23:10:33 -06:00
Šarūnas Nejus
0682d0d030
test_play: Remove files generated by play plugin 2024-07-02 15:35:24 +01:00
Šarūnas Nejus
1fda7b6111
fetchart, artresizer: Create art files in predictable directories
This allows to clean them up in art (1) fetching, (2) resizing and (3)
deinterlace tests.
2024-07-02 15:35:24 +01:00
Max Goltzsche
90f0ae2d93
fetchart: defer file removal config option eval
Defer the evaluation of the source file removal options (`import.delete` and `import.move`) to the point where the fetchart plugin is actually called instead of only evaluating those configuration options on plugin initialization.
This is to allow other plugins (such as the [ytimport](https://github.com/mgoltzsche/beets-ytimport/blob/v1.8.1/beetsplug/ytimport/__init__.py#L194) plugin) to invoke the import directly (within the same python process; implicitly invoking the fetchart plugin) with temporarily overwritten configuration options.

Relates to https://github.com/beetbox/beets/issues/5167#issuecomment-2106465172
2024-06-26 01:53:04 +02:00
Serene
de0742b9d6
Merge branch 'master' into bug-fetchart-resize-logic-fix 2024-06-25 14:30:39 +10:00
Šarūnas Nejus
4e06b59b60
Filter albums by tracks fields and vice versa (#5327)
Fixes #4360

This PR enables querying albums by track fields and tracks by album
fields, and speeds up querying albums by `path` field.

It originally was part of #5240, however we found that the changes
related to the flexible attributes caused degradation in performance. So
this PR contains the first part of #5240 which joined `items` and
`albums` tables in queries.
2024-06-25 02:04:45 +01:00
Arkadiy Illarionov
1c020b8264 Check Python version instead of catching ImportError 2024-06-22 19:55:52 +03:00
Arkadiy Illarionov
859072ee9e Use typing_extensions only when needed
Self was added in Python 3.11
TypeAlias was added in Python 3.10
2024-06-22 16:02:24 +03:00
Šarūnas Nejus
981a61bd56
Add support for filtering relations 2024-06-21 15:05:29 +01:00
Šarūnas Nejus
2800a323a2
Revert "Make queries fast, filter all flexible attributes (#5240)"
This reverts commit 143b9202f3, reversing
changes made to 8508a57d77.
2024-06-19 21:51:44 +01:00
Šarūnas Nejus
2c4b42d167
Keep the same FieldQuery.field interface as before 2024-06-17 08:59:20 +01:00
Šarūnas Nejus
1f93207823
Add any_writable_field_query to fix bpd search 2024-06-16 15:28:46 +01:00
Šarūnas Nejus
e0c50c5501
Remove slow lookups from beetsplug/aura
It seems like previously filtering by flexible attributes did not work
- I'd receive '{"data": []}' trying to GET `/aura/tracks?filter[play_count]=11`

Now this works, not only for tracks, but for `/aura/artists` and
`/aura/albums` too.

Additionally, this improves `/aura/tracks` response time significantly.
I tried loading the default of 500 tracks from my library:

On `master`, it took ~20s
After this commit, it takes under 1s.
2024-06-16 15:28:46 +01:00
Šarūnas Nejus
b0154d5cde
Fix querying fields present in both tables 2024-06-16 15:28:45 +01:00
Šarūnas Nejus
19470f3d76
Use native os.cpu_count
os.cpu_count has only been available since Python 3.4
2024-06-16 00:52:38 +01:00
Šarūnas Nejus
f388ff6ec1
Replace py3_path with builtin os.fsdecode
`os.fsdecode` has only been available since Python 3.2.
2024-06-16 00:52:37 +01:00
Vrihub
e6b773561b Refactor regexps in PATTERNS 2024-06-15 20:58:41 +02:00
Vrihub
09660426a8 Logging: add message about the pattern being tried 2024-06-15 20:56:40 +02:00
Vrihub
b5216a06f4 Proposed fix for issue #5218
Check for existence of "title" matching group before using it
2024-06-15 20:52:55 +02:00
Arav K.
549fea14cd [util/pipeline] use 'Queue' without generics
Apparently Python 3.8 does not support generic type parameters on the
built-in 'queue.Queue' type?
2024-06-12 15:59:31 +02:00
Arav K.
d3bdf137ea Resolve some 'mypy' errors 2024-06-12 15:58:00 +02:00
Šarūnas Nejus
0966e3c653
Test aura (#5239)
In #4746 I was making a small adjustment in beetsplug/aura.py and found
that the module wasn't tested. So this PR adds some high-level tests to
act a safeguard for any future adjustments.
2024-06-10 13:07:48 +01:00
Šarūnas Nejus
179405ed82
A couple of small updates in the code 2024-06-05 04:41:02 +01:00
Lev Gorodetskiy
2a4cf0d335
Codestyle 2024-05-26 11:57:24 -03:00
Lev Gorodetskiy
2130404217
Add timeout to all requests calls 2024-05-25 12:24:53 -03:00