15250 Commits

Author SHA1 Message Date
Šarūnas Nejus
12c9f4233f typing: type and refactor duplicates plugin (#6947)
Part of #6924.

### What changed

- Types added to `beetsplug/duplicates.py`.
- Refactors `beetsplug/duplicates.py` so album and item commands now
share one execution path through `_run_command()`.
- Replaces the old generic `_merge()` dispatch with an explicit
`merge_func` passed into `_duplicates()`, so album and item merge
behavior stays separate but simpler.
- Tightens typing across the plugin by using clearer `Album`, `Item`,
`AlbumOrItem`, and `LibModel` annotations in the main helper methods.

### Why it matters

- The plugin's architecture is now more direct: command setup decides
`'albums'` vs `'items'`, and the shared duplicate-processing flow
handles the rest.
- This removes branching and type ambiguity from the core duplicate
logic, which makes the code easier to follow and safer to change.
- Functional behavior stays the same at a high level, but the plugin is
now easier to maintain, reason about, and extend.
2026-08-31 11:26:48 +01:00
Šarūnas Nejus
6dbb4ce756 typing: fix types and refactor duplicates plugin 2026-08-31 11:19:31 +01:00
Šarūnas Nejus
4a99a991cb Add commits to git blame ignore 2026-08-31 11:19:27 +01:00
Šarūnas Nejus
8e4fa56acb typing: add types to duplicates plugin 2026-08-31 11:01:12 +01:00
Šarūnas Nejus
301677ad00 typing: type mpdstats, thumbnails plugins (#6939)
Part of #6924.

- This PR is a typing and interface cleanup for `beetsplug/mpdstats.py`
and `beetsplug/thumbnails.py`. It does not add new features; it makes
the code's internal contracts clearer and more explicit.

- In `mpdstats`, MPD responses and `now_playing` state now have concrete
types, path lookup uses the correct beets path shape, and player event
handling is narrowed to known states. This makes the control flow easier
to follow and reduces hidden assumptions in the plugin.

- In `thumbnails`, the code stops passing full `Album` objects where
only `album.path` or `artpath` is needed. That separates album metadata
from file-path operations, removes ambiguity around optional
`album.artpath`, and makes thumbnail generation steps more direct.

- Tests were updated to match the narrower method signatures and the
corrected path handling.

- High-level impact: safer internal APIs, clearer data flow, and lower
risk of type-related bugs. The only visible behavior change is that
missing album art is now logged as a warning instead of info.
2026-08-31 11:00:44 +01:00
Šarūnas Nejus
9611a23a87 typing: fix annotations in thumbnails 2026-08-31 10:55:33 +01:00
Šarūnas Nejus
6b4f8fa25b typing: add annotations to thumbnails 2026-08-31 10:55:33 +01:00
Šarūnas Nejus
b64c693252 typing: fix annotations in mpdstats 2026-08-31 10:55:33 +01:00
Šarūnas Nejus
4f0ac15b99 typing: add annotations to mpdstats 2026-08-31 10:55:33 +01:00
Šarūnas Nejus
7d67ca0c75 typing: type metadata source plugins: discogs, deezer, beatport, spotify chroma, listenbrainz, mbsubmit, spotify (#6938)
Part of #6924.

- This change tightens typing across metadata source plugins and shared
request/MusicBrainz utilities, so plugin code lines up better with the
data it actually consumes.

- Biggest architecture change is in `beetsplug/_utils/musicbrainz.py`:
common release fields move into `BaseRelease`, and new
`RecordingWithReleases` plus `get_recording_with_releases()` make
`'recording with releases'` an explicit shape instead of loose dict
guessing.

- Plugin code in `listenbrainz`, `chroma`, `acousticbrainz`, `beatport`,
`deezer`, `spotify`, `discogs`, and `mbsubmit` is then updated to use
clearer return types, typed collections, and safer access patterns for
optional API data.

- High-level impact: less ambiguity at plugin boundaries, better
editor/type-checker help, and fewer runtime mistakes from assuming
fields exist when remote APIs may omit them.

- There is also a small cleanup in `mbsubmit`: output formatting now
goes through `format()` with a cached config value, which removes a
cross-plugin helper dependency and keeps the command simpler.
2026-08-31 10:55:05 +01:00
Šarūnas Nejus
4e1fdf0bf3 typing: fix annotations in metadata source plugins 2026-08-31 10:48:16 +01:00
Šarūnas Nejus
21e045795d typing: add annotations to metadata source plugins 2026-08-31 10:48:16 +01:00
Šarūnas Nejus
0f27b2964c web: Remove typing t alias 2026-08-31 10:48:13 +01:00
Šarūnas Nejus
db3680c501 typing: type aura, web plugin and export plugin (#6937)
Part of #6924.

- This change tightens typing across `beetsplug/aura.py`,
`beetsplug/export.py`, and `beetsplug/web/__init__.py` so plugin
interfaces better match real runtime behavior.

- High-level impact: no big feature shift, but architecture become more
explicit. Plugin boundaries, data shapes, and format handling are
clearer, which should make future changes safer and type errors easier
to catch.

#### `aura`

- base document methods are more explicit
- request args are lined up with `MultiDict`
- add safer handling for missing included resources.

I could not resolve typing without using `type: ignore` in two places
and I found both pieces of logic should be removed:

1. Field value conversion
- It caused `sqlite3.InterfaceError` when using `filter[genres]`,
because it was split into, e.g. `"genres" -> ["g", "e", "n", "r", "e",
"s"]`.
- The rest of the field types are either `str` and `int`. `MatchQuery`
happily accepts a `str` pattern when matching `int` fields.
2. When sorting by a `field`, excluding empty/null values: I'm pretty
sure this is not the behaviour we want 😅.

Extended tests to prove both of the above.

#### `export`

- simplify format selection and moves export flow to a single
iterable-based pipeline
- `json`, `jsonlines`, `csv`, and `xml` now share one higher-level data
path, with path-like byte fields normalized before writing

#### `web`

- add clearer endpoint and decorator annotations
- a small safety fix for missing item files returning `404` instead of
failing later
2026-08-31 10:29:01 +01:00
Šarūnas Nejus
21ffeff202 aura: fix filtering and sorting 2026-08-29 21:15:38 +01:00
Šarūnas Nejus
526f6ec4ba typing: fix API and export plugins 2026-08-29 21:15:38 +01:00
Šarūnas Nejus
df767c7602 Add commit to git blame ignore 2026-08-29 21:15:38 +01:00
Šarūnas Nejus
ee4d6eef36 typing: add annotations to API and export plugins 2026-08-29 21:10:29 +01:00
Šarūnas Nejus
e7a788fd37 typing: type bpd plugin (#6936)
Part of #6924.

- This PR makes the `beetsplug.bpd` server stack easier to understand by
adding explicit type annotations across the main layers: `BaseServer`,
`Server`, `Connection`, `MPDConnection`, `ControlConnection`, `Command`,
and `GstPlayer`.

- At an architectural level, it clarifies how data moves through the
plugin:
  - command handlers now have clearer input and return types,
- shared server state like `playlist`, `connections`, and notification
sets is declared directly,
- internal playback paths that do not come from a live client are
modeled explicitly with `MPDConnection | None`.

- It also includes a few small refactors that support that clearer
structure without changing the design:
- `_parse_range` is moved into `BaseServer` so the shared logic is
actually available where it is used,
  - command parsing is simplified,
  - socket reads now separate raw bytes from decoded text,
  - VFS and library lookups are guarded more carefully.

- High-level impact: this is mainly a maintainability and correctness
change, not a feature change. It makes internal interfaces in `bpd` more
explicit, improves static type checking, and reduces the chance of
type-related bugs while keeping the plugin's overall behavior and
architecture the same.
2026-08-29 21:09:45 +01:00
Šarūnas Nejus
a5080942c8 Add commit to git blame ignore 2026-08-29 17:49:13 +01:00
Šarūnas Nejus
743eb3b36b bpd: fix search when any field is used 2026-08-29 17:49:13 +01:00
Šarūnas Nejus
bfe5ba51c5 typing: fix annotations in bpd plugin 2026-08-29 17:49:13 +01:00
Šarūnas Nejus
1ae8778385 typing: add annotations to bpd plugin 2026-08-29 17:38:48 +01:00
Šarūnas Nejus
31c917b8c8 ui: remove do_query to tighten input_select_objects (#6942)
Part of #6924.

Closes #2802.
Closes #4880.

## Summary

- Fix `modify` interactive selection by displaying the changes already
prepared for each object and syncing only the objects returned by
`ui.input_select_objects`. Previously, the selection prompt could apply
the pending mutations again, producing incorrect output or raising a
`TypeError`.
- Fix `move --album --timid` when choosing individual albums. The
selection preview now uses the command's model-specific path expansion,
so it displays the album's item paths instead of trying to read
item-only fields from an `Album`.
- Remove `beets.ui.commands.utils.do_query` and let `modify`, `move`,
`remove`, `update`, `write`, and `edit` own their item and album queries
and no-match behavior.
- Keep thin model-specific query wrappers where mypy needs help
preserving item and album types. Shared `modify` parameters use
`Unpack[TypedDict]` so callers remain checked without duplicating the
parameter list.
- Narrow the return type of `ui.input_select_objects` from `Any` to
`Sequence[T]` and keep `remove` confirmation counts accurate.
- Document both user-facing fixes in the changelog.

## Tests

- Add CLI regression coverage for `modify` selection output and timid
album selection in `move`.
- Update command tests for `fields`, `import`, `list`, `move`, `remove`,
and `update` to exercise their CLI entrypoints, including selective
removal and confirmation counts.
- Remove the obsolete `do_query` unit tests with the helper.
- GitHub checks pass for linting, formatting, typing, docs, CodeQL, base
installation, all Ubuntu Python 3.11-3.14 jobs, and Windows Python 3.13.
The remaining matrix jobs were still running when checked.
2026-08-27 09:50:16 +01:00
Šarūnas Nejus
03877cc20b move: fix moving albums in timid mode 2026-08-27 06:47:40 +01:00
Šarūnas Nejus
e5846915d4 modify: fix selecting objects 2026-08-27 06:47:39 +01:00
Šarūnas Nejus
efc7e4f206 remove: fix ui formatting and test it 2026-08-27 06:47:39 +01:00
Šarūnas Nejus
2971393618 Replace do_query with model-specific logic
This allows to define a proper return type for input_select_objects.
2026-08-27 06:47:39 +01:00
Šarūnas Nejus
dd841f6b99 Test commands by CLI invocation 2026-08-27 01:31:43 +01:00
Šarūnas Nejus
316cf47a47 Add missing commit from the previous PR to git blame ignores 2026-08-27 01:31:40 +01:00
Šarūnas Nejus
a3268e68de typing: type beets core (#6941)
Part of #6924.

- This PR tightens typing across core surfaces in `beets`, especially
around `ui` command handling, `library` models, `autotag`, `util`,
logging, and test helpers.

- Architecturally, the change makes several implicit contracts explicit:
- `ui` command parsers and subcommands now have clearer typed
interfaces.
- `library` removal flow is split into internal `_remove()` and public
`remove(...)`, which better separates shared database-change behavior
from model-specific delete logic.
- Utilities like `fix_extension()` and playlist helpers now expose more
precise return/value types.

- High-level impact is mostly safety and maintainability rather than new
functionality. The goal is to make core APIs easier to reason about,
easier to type-check, and less likely to drift between declared and
actual behavior.

- There are a few small behavioral hardening changes:
- `Distance` arithmetic/comparison now rejects unsupported operand types
instead of silently accepting invalid values.
- Some CLI and command code paths were adjusted to make album/item
branching explicit and type-safe.
- A few test fixtures and helpers were updated to match the stricter
contracts.

- Reviewer takeaway: this is primarily a core typing cleanup with light
refactoring, aimed at improving internal API clarity and catching
mistakes earlier, with only limited runtime behavior changes in edge
cases.
2026-08-25 00:02:41 +01:00
Šarūnas Nejus
01375c7392 typing: fix core annotations 2026-08-22 17:49:49 +01:00
Šarūnas Nejus
029de94b20 typing: add missing core annotations 2026-08-22 17:49:49 +01:00
Šarūnas Nejus
07f39c5204 lyrics: don't match LRCLib entries that have no lyrics (#6900)
## Description

LRCLib stores track metadata independently of the lyrics themselves, so
an entry can come back with both `plainLyrics` and `syncedLyrics` null
while `instrumental` is still `False`.

`LRCLyrics.is_valid` accepted such an entry as a match on duration
alone, and `get_text` then returned `self.plain`, i.e. `None`, despite
being annotated `-> str`. The `None` propagated into `Lyrics`, and the
first access of its text raised:

```
AttributeError: 'NoneType' object has no attribute 'splitlines'
  File "beets/util/lyrics.py", line 108, in _split_lines
    for line in self.text.splitlines()
```

`beet lyrics` surfaces this per track, but during an **import** the
exception escapes the pipeline stage and aborts the entire run, so one
such track strands every file queued behind it. Instrumental-heavy
material (lo-fi, game soundtracks, ambient) hits it often.

### Reproducing

Real response from the public API,
`https://lrclib.net/api/search?track_name=Anther&artist_name=Blue%20Wednesday`:

```json
{
  "id": 37048543,
  "trackName": "Anther",
  "artistName": "Blue Wednesday",
  "duration": 189.0,
  "instrumental": false,
  "plainLyrics": null,
  "syncedLyrics": null
}
```

Against `master`, with no configuration involved:

```
candidate.is_valid  = True     <- accepted as a match
get_text() returned = None     <- annotated `-> str`
AttributeError: 'NoneType' object has no attribute 'splitlines'
```

## Changes

- An entry with no lyrics text at all is no longer a valid match, so the
search continues to other candidates and backends and ultimately reports
that no lyrics were found. This is deliberately kept distinct from an
instrumental track, where "no lyrics" is itself the answer and the
existing `instrumental` handling is unchanged. Marking these as
instrumental would assert something the API response does not tell us:
the lyrics may simply not have been contributed yet.
- A null `plainLyrics` now falls back to synced lyrics rather than
discarding lyrics that are present.
- `LRCLibAPI.Item.plainLyrics` and `LRCLyrics.plain` are annotated as
nullable, matching what the API actually returns.
2026-08-22 17:46:44 +01:00
Šarūnas Nejus
d9e2b65758 Merge branch 'master' into fix/lrclib-null-lyrics 2026-08-22 17:41:55 +01:00
Šarūnas Nejus
096106d0e1 typing: add types to command handlers (#6932)
First part of #6924.

## Summary

- Give command handlers across core commands and many plugins a
consistent `Library`, options, and `list[str]` interface.
- Describe command-specific options with small `Protocol` types where
handlers depend on particular flags, while retaining `optparse.Values`
for handlers that do not need a narrower shape. This makes the CLI
boundary easier for type checkers to follow without coupling handlers to
a concrete options container.
- Broaden `beets.dbcore.sort` from `list` to `Sequence` and tighten
several item/album collection annotations to match the data actually
passed between APIs.
- Fix small runtime mismatches exposed while making these types
concrete:
- `beetsplug.ipfs` invokes `PlayPlugin._play_command` with the expected
options shape and closes remote libraries after use.
- `beetsplug.bpd` reads `control_port` through the config API that
matches its actual value type.
- `beets.ui.commands.update` handles excluded fields, missing prior
items, optional flags, and byte paths safely.
- Command option defaults and config handoffs are normalized where their
runtime values can be optional.

## Tests

- Add regression coverage for IPFS playback through the Play plugin and
context-managed remote libraries.
- Refresh lyrics integration fixtures for the current LRCLIB and LRCGET
responses.

Most changes are annotations and interface clarification. The behavioral
changes are limited to the mismatches listed above.
2026-08-21 21:07:31 +01:00
Šarūnas Nejus
467cf7a8b7 Fix lyrics integration tests 2026-08-21 20:56:39 +01:00
Šarūnas Nejus
914441da16 typing: fix command handlers types 2026-08-21 20:56:38 +01:00
Šarūnas Nejus
a3bb963ea0 ipfs: fix play command 2026-08-20 00:51:44 +01:00
Šarūnas Nejus
bf576238c8 Add commit to blame ignore 2026-08-20 00:51:44 +01:00
Šarūnas Nejus
6241139600 typing: define explicit optparse.Values for each command 2026-08-19 23:57:05 +01:00
Šarūnas Nejus
14eabc6060 typing: add types to command handlers 2026-08-19 23:57:01 +01:00
Šarūnas Nejus
293ae110c7 typing: type utils and modules that are mostly missing annotations (#6925)
Fixes: https://github.com/beetbox/beets/issues/6923

### What changed

- This PR is mostly a typing and module-boundary cleanup.
- Code now prefers public package exports like `beets.library` and
`beets.dbcore` instead of reaching into deeper internal modules.
- Shared model typing was renamed from `AnyLibModel` to `AlbumOrItem`,
which makes intent clearer where code handles either an `Album` or an
`Item`.
- Several typing fixes were added around `import`, `embedart`,
`_utils.art`, and `beets.util.functemplate`.

### Architecture impact

- The main architectural shift is toward using stable, package-level
APIs such as `beets.library` and `beets.dbcore` as the import boundary.
- `dbcore.Results` now behaves like a `Sequence`, which lets callers
depend on a simpler, more general interface instead of a concrete
internal result type.
- `beets.util.functemplate` got a deeper type pass and some small
internal cleanup, but its role in the system stays the same.

### High-level impact

- Improves type safety and IDE support across importer, library, plugin,
and template code.
- Reduces coupling to internal module layout, which should make future
refactors safer.
- Makes a few core interfaces easier to understand and reuse, especially
around library model collections and import-session callbacks.
- Overall, this looks like low-risk maintenance work with small
correctness improvements and no intended feature change.
2026-08-18 23:28:52 +01:00
Šarūnas Nejus
c4953067cb Add commits to blame ignore 2026-08-18 23:22:16 +01:00
Šarūnas Nejus
33a01fe0b3 typing: rename AnyLibModel -> AlbumOrItem 2026-08-18 23:20:39 +01:00
Šarūnas Nejus
27900f58fb Prefer imports from modules that provide import aliases 2026-08-18 23:20:39 +01:00
Šarūnas Nejus
f37306b0cc typing: remove redundant types 2026-08-18 23:20:39 +01:00
Šarūnas Nejus
0cae01cad2 typing: fix types in beets.util.functemplate 2026-08-18 23:20:39 +01:00
Šarūnas Nejus
d8fc064e9c typing: add types to beets.util.functemplate 2026-08-18 23:20:39 +01:00
Šarūnas Nejus
d143e2cb92 typing: fix types under beets.ui.commands.import_ 2026-08-18 23:20:39 +01:00