Proposal: better import mode configuration
- Dominant language
- Python
- Stars
- 15.7k
- Forks
- 2.1k
- Avg merge
- 4d 21h
- Merged PRs (30d)
- 31
Description
This issue aims to discuss the improvement of the UX for configuring file operations during beets import.
# Background
Beets supports many ways of importing files:
- use the files in their current location (in place)
- copy
- move
- link (symlink)
- hardlink
- reflink
To configure them, we have as many [boolean config options](https://beets.readthedocs.io/en/stable/reference/config.html#importer-options).
Some of these modes can be overridden via the CLI with [flags](https://beets.readthedocs.io/en/stable/reference/cli.html#import).
# Problems
While modes are mutually exclusive (it does not make sense to both copy and move, or both move and hardlink), we allow enabling multiple of them at the same time.
This leads to the following issues:
## Unclear behavior
Beets does not complain when multiple modes are enabled at the same time. It simply picks one using a [priority system](https://github.com/beetbox/beets/blob/master/beets/importer/session.py#L125) without telling the user. This can lead users to believe one mode is active while beets silently applies another. This behavior is partially documented, but not for all options. For example:
> **reflink**: [...] The option is ignored if move is enabled (i.e., beets can move or copy files but it doesn’t make sense to do both).
This is not quite true, it will be also ignored if link or hardlink are enabled.
Figuring out what beets will do requires factoring in the defaults and priorities. This is even more confusing when using CLI flags as one needs to know what exactly is in their config when running the command and factor that in as well.
## Configuration bloat
Each of these modes have their own config option, making the config system and the documentation more complex and verbose.
This complexity is even more pronounced in the CLI where we need both an ON and OFF flag for each of the modes we want to support.
## In place, the forgotten child
When all modes are turned off, beets just uses the files where they are, without copying, moving, or anything.
Users may assume that one of the options needs to be enabled and not realize that turning them all off enables another, "hidden" mode: use the original files in place.
# Proposal
Replace current boolean flag with a single enum:
- `inplace`
- `copy` (default)
- `move`
- `link`
- `hardlink`
- `reflink`
Exactly one mode may be selected.
### Benefits
- **clarity**: only one mode can be configured at a time, CLI overrides are way simpler. The precedence system is removed entirely.
- **discoverability**: users can now see at a glance in the documentation or in the CLI help what are their options.
- **validation**: we now have a clear set of valid values, everything else is an error.
- **documentation**: documentation is easier to write now that everything is a single option
- **CLI ergonomics**: this cuts the number of CLI flags from today's 4 (potentially 10 if all modes were available as CLI flags with the current implementation) to just one.
- **future extensibility**: adding another mode means just adding a new enum value.
### Config
```yaml
import:
mode: move
```
### CLI
```shell
beet import --mode move
beet import --mode inplace
```
Sadly, `-m` and `-M` are already used for current flags and cannot be used as a shorthand for the new `--mode`. We may not need one as this is a relatively advanced and niche use case.
# Deployment plan
## Phase 1
This first phase is about introducing this new mode of operation without breaking current user setups. Plugins or external tooling relying on legacy boolean settings would continue to function during Phase 1.
### Code
New config option and flag are introduced.
The following handling is applied:
| | old setting(s) set | old setting(s) unset |
| -------------------- | ------------------------------------------------ | -------------------- |
| **new `mode` set** | error **\*** | use new `mode` |
| **new `mode` unset** | compute mode from old settings, warning **\*\*** | `mode` = copy |
- **error\***: Mixing old and new configuration styles is rejected to avoid ambiguous behavior and simplify migration.
- **warning\*\***: Compute the `mode` value equivalent to the user config and display a warning indicating that the settings they are using are deprecated and that they should switch to `mode: $computed_value`
Users are allowed to override old settings with the new `--mode` flag.
### Documentation
New config and flag are documented. Previous settings and flags are marked as deprecated and redirect to their new replacements.
## Phase 2
Removal of old settings and flags.
Contributor guide
Research direction
Read beets/importer/session.py around line 125 and review the importer configuration and import CLI option documentation linked in the issue. Confirm how legacy boolean settings and flags are currently combined, then define the migration behavior and tests needed for both deployment phases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- cli, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100