fixes#6104
## Description
When using the `fromfilename` and `edit` plugins together during import,
aborted edit sessions could silently discard the temporary tags injected
by `fromfilename` (e.g., track number and title derived from the
filename). This happened when using `eDit` or `edit Candidates` and then
cancelling: the edit plugin reverted objects by re-reading from disk,
which does not contain the `fromfilename`-generated metadata.
This PR changes the `edit` plugin so that cancel and “continue Editing”
both roll back objects to the original in-memory snapshot captured
before opening the editor, instead of reloading from the files. This
preserves temporary tags provided by other plugins (like `fromfilename`)
across aborted edit sessions, while still only writing to disk when the
user chooses Apply.
`importer_edit` is also updated to rely on this rollback behavior when
edits are cancelled, rather than re-reading from disk, so interactive
imports resume with the same in-memory metadata they started with.
Prevents a crash when "skip" is selected in the importer and
`task.imported_items()` runs into a condition branch that supposedly
should never be reached:
```
File "beets/beets/importer/tasks.py", line 254, in imported_items
assert False
^^^^^
AssertionError
```
- Since for items/albums that should be skipped, looping through
`task.imported_items()` is not required anyway, the fix here is to exit
early from the function that calls it.
- Additionally this PR fixes the original changelog entry which was
located at an older releases "new features list". Also now it briefly
explains to changelog readers what the plugin actually does.
- Two new tests were added that proof that "skip doesn't crash" and
reimports never "suggest removal of source files"
---------
Co-authored-by: Doron Behar <doron.behar@gmail.com>
This PR refactors the MusicBrainz plugin implementation by replacing the
`musicbrainzngs` library with direct HTTP API calls using `requests` and
`requests-ratelimiter`.
**Key Changes:**
- **New utilities module**: Added `beetsplug/_utils/requests.py` with
`TimeoutSession` class and HTTP error handling (`HTTPNotFoundError`,
`CaptchaError`)
- **MusicBrainz API rewrite**: Replaced `musicbrainzngs` dependency with
custom `MusicBrainzAPI` class using direct HTTP requests
- **Rate limiting**: Integrated `requests-ratelimiter` for API rate
limiting instead of `musicbrainzngs.set_rate_limit()`
- **Data structure updates**: Updated field names to match MusicBrainz
JSON API v2 format (e.g., `medium-list` → `media`, `track-list` →
`tracks`)
- **Dependency management**:
- Made `musicbrainzngs` optional and added it to plugin-specific extras
(`listenbrainz`, `mbcollection`, `missing`, `parentwork`). Updated
plugin docs accordingly.
- Made `requests` a required dependency to ensure backwards
compatibility (ideally, we would make it an optional dependency under
`musicbrainz` extra).
- **Error handling**: Simplified error handling by removing
`MusicBrainzAPIError` wrapper class
**Benefits:**
- Direct control over HTTP requests
- Consistent rate limiting across all network requests
- Better alignment with modern MusicBrainz API responses
The changes maintain backward compatibility while modernizing the
underlying implementation.
Fixes#5553Fixes#5095
See this line in https://musicbrainz.org/doc/MusicBrainz_API#Lookups
> To include more than one subquery in a single request, separate the arguments to inc= with a + (plus sign), like inc=recordings+labels.
Introduce a new RequestHandler base class to introduce a shared session,
centralize HTTP request management and error handling across plugins.
Key changes:
- Add RequestHandler base class with a shared/cached session
- Convert TimeoutSession to use SingletonMeta for proper resource
management
- Create LyricsRequestHandler subclass with lyrics-specific error
handling
- Update MusicBrainzAPI to inherit from RequestHandler
- Add preserving strings that are all lowercase or all upper case
- Fix spelling of 'separator' in config, docs and code
- Move most of the logging for the plugin to debug to keep log cleaner.
Improvements I found a need for in my daily use with the plugin.
- [x] Documentation. (If you've added a new command-line flag, for
example, find the appropriate page under `docs/` to describe it.)
- [x] Changelog. (Skipping as the plugin has not been released yet)
- [x] Tests. (Very much encouraged but not strictly required.)
## Description
Fixes#6183
The problem was that the plugin was combining multiple queries using
`OrQuery`, which didn't preserve the order in which queries were
specified in the configuration.
## Description
Fixes#5625
When `convert.never_convert_lossy_files` is enabled, `beet convert` was
ignoring the explicit `--format` option and just copying the lossy files
without
transcoding them. For example:
- `beet convert format:mp3 --format opus`
would still produce MP3 files instead of OPUS.
Change:
- Allows to override options `never_convert_lossy_files`, `max_bitrate`
or `no_convert` for `beet convert` as well as trying to convert to the
same format as existing already with a new option `--force`. That way,
for example lossy files selected by the query are transcoded to the
requested format anyway.
- Keeps existing behavior for automatic conversion on import (no CLI
override there).
- Adds tests to cover checking whether `--force` correctly overrides
settings or CLI options.
- Documents the behavior in the convert plugin docs
Co-authored-by: J0J0 Todos <jojo@peek-a-boo.at>
This pull request addresses an issue where control characters in log
messages could halt beets execution entirely. The fix implements
sanitization of log messages by removing C0 and C1 control characters
before they reach the terminal.
Added regex pattern to strip C0/C1 control characters (excluding useful
whitespace) from log messages before terminal output. This prevents
disruptive/malicious control sequences from affecting terminal
rendering.
Fixes: #6121
This PR introduces a centralized deprecation system and adjusts
`musicbrainz` plugin loading to properly handle the deprecated
`musicbrainz.enabled` configuration option.
#### MusicBrainz
- Added deprecation warnings for the `musicbrainz.enabled` configuration
option:
- When set to `true`, warns users to explicitly add `musicbrainz` to
their `plugins` configuration and adds it if not already present
- When set to `false`, warns users and adds the plugin to
`disabled_plugins` (list
received by the `--disable-plugins` flag)
#### Deprecations
- Created new `beets/util/deprecation.py` module with standardized
deprecation helpers:
- `deprecate_for_user()` - logs warnings visible to end users
- `deprecate_for_maintainers()` - emits `DeprecationWarning` for
developers
- `deprecate_imports()` - handles deprecated module imports with
automatic version calculation
- `_format_message()` - generates consistent deprecation messages that
auto-calculate next major version
- Migrated all deprecation handling to use the new centralized
functions:
- Replaced inline `warnings.warn()` calls throughout codebase
- Updated `deprecate_imports()` signature to remove explicit `version`
parameter
- Converted user-facing deprecation warnings in plugins to use
logger-based `deprecate_for_user()`