Prevents this crash:
```
$ beet import ~/Music/Music/_/[1405]/00.mp3
Traceback (most recent call last):
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/bin/.beet-wrapped", line 9, in <module>
sys.exit(main())
~~~~^^
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/ui/__init__.py", line 1859, in main
_raw_main(args)
~~~~~~~~~^^^^^^
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/ui/__init__.py", line 1838, in _raw_main
subcommand.func(lib, suboptions, subargs)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/ui/commands.py", line 1390, in import_func
import_files(lib, byte_paths, query)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/ui/commands.py", line 1330, in import_files
session.run()
~~~~~~~~~~~^^
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/importer/session.py", line 234, in run
pl.run_parallel(QUEUE_SIZE)
~~~~~~~~~~~~~~~^^^^^^^^^^^^
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/util/pipeline.py", line 471, in run_parallel
raise exc_info[1].with_traceback(exc_info[2])
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/util/pipeline.py", line 336, in run
out = self.coro.send(msg)
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/util/pipeline.py", line 219, in coro
func(*(args + (task,)))
~~~~^^^^^^^^^^^^^^^^^^^
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/importer/stages.py", line 141, in lookup_candidates
plugins.send("import_task_start", session=session, task=task)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/plugins.py", line 505, in send
result = handler(**arguments)
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beets/plugins.py", line 200, in wrapper
return func(*args, **kwargs)
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beetsplug/fromfilename.py", line 165, in filename_task
apply_matches(d, self._log)
~~~~~~~~~~~~~^^^^^^^^^^^^^^
File "/nix/store/lfv9ns20hz2bg6d44js378vcxjfm9261-beets-2.3.1/lib/python3.13/site-packages/beetsplug/fromfilename.py", line 124, in apply_matches
item.title = str(d[item][title_field])
~~~~~~~^^^^^^^^^^^^^
KeyError: 'title'
```
Simplify wording by replacing flowery or pretentious terms throughout the project.
A bouquet refers to trees or flowers. The etymology is pretty clear on
the Latin root via French.
Co-authored-by: Sebastian Mohr <sebastian@mohrenclan.de>
- Switch default `text_diff_added` color from bold **red** to bold
**green**; unify all diff/case comparisons to use `text_diff_added` and
`text_diff_removed` colors consistently.
- Simplify color handling and setup: introduce cached color config
(validation + legacy normalization), consolidate regexes
- Remove unused colors names
- Update `ui` configuration docs.
### `beet write -p year:2000..2005`
#### Before
<img width="1261" height="460" alt="image"
src="https://github.com/user-attachments/assets/70207350-6d9a-48d8-a314-457ac07c5ad1"
/>
#### After
<img width="1257" height="536" alt="image"
src="https://github.com/user-attachments/assets/efcf3453-016a-490f-84ab-dedfb2aca97b"
/>
### `beet move -p albumtype:broadcast`
#### Before
<img width="1103" height="505" alt="image"
src="https://github.com/user-attachments/assets/9e76c5d6-d878-4535-9da3-a949e6c0830c"
/>
#### After
<img width="1134" height="508" alt="image"
src="https://github.com/user-attachments/assets/3dc03988-8011-4072-8840-6f0a0d12350f"
/>
## Summary by Sourcery
Improve default diff colors from red to green and streamline color
handling by refactoring ANSI code management, removing legacy logic, and
unifying diff highlighting. Also extract a cached changed_prefix
property and update UI config documentation.
Enhancements:
- Introduce cached get_color_config and consolidate ANSI escape regex
patterns to simplify color configuration parsing
- Refactor diff highlighting to consistently use text_diff_added and
text_diff_removed and simplify _colordiff implementation
- Add ChangeRepresentation.changed_prefix cached property for consistent
‘not equal’ prefix formatting
Documentation:
- Update UI configuration documentation to reflect new default colors
and removed settings
Chores:
- Remove unused color names and legacy normalization code
Addresses some fixes and additions mentioned here #5915 and some I
collected myself over the years.
Of course genres and sub-genres classifications always are rather
opinionated, but I consider them useful for others too. Critical
feedback is welcome.
experimental, even though a tag last.fm very often returns (in top 20
tag charts!), it is too broad of a term to be pinned downed with any
particular genre, thus can't really be used for canonicalization.
Fixes to the beets default tree and whitlist files I collected over the
years; Includes Tags last.fm returns quite often; Also the
chart.getTopTags API endpoint was checked to make sure the top 100
charts are included in beets default tree and whitelist.
I split out this refactor from #5744 to make the blacklist PR just a
little bit easier to read.
- Separate _load methods for whitelist and canonicalization tree file
loading.
- Add return type hints for those methods.
- Use `pathlib.Path()` instead of `normpath()`
- Additionally respect None value to fall back to built-in settings for
`canonical` and `whitelist` files (was only `""` before) - this code
exists for backwards compatibility (see
233f71a457),
and might be better sanity checked and vanish in the future... (a None
value is set eg. `canoncial:`) -> #5994
instead of codecs.open(), which most probably is a relict of beets' Python2/3 compatibility area.
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
### Context
See https://github.com/beetbox/beets/pull/5916 where we've come across a
need to define common logic between `TrackInfo` and `AlbumInfo`.
### Changes
- Introduce generic `Info` base (extends `AttrDict`) used by `AlbumInfo`
/ `TrackInfo` to centralize shared attributes and initialisation logic.
- Sort keyword parameters in each constructor alphabetically and make
them explicit.
- Deduplicate and simplify shared `copy()` method using `copy.deepcopy`
- Improve type hints and documentation.
- Drop unused logging artifacts.
## Summary by Sourcery
Refactor metadata-handling classes by extracting common functionality
into a new Info base, updating AlbumInfo and TrackInfo to extend it with
explicit sorted parameters, unify their copy logic, improve type
annotations and docs, and drop obsolete logging code
New Features:
- Introduce a generic Info base class to centralize shared logic for
AlbumInfo and TrackInfo
Enhancements:
- Alphabetically sort and explicitly declare constructor keyword
parameters for consistency
- Unify and simplify the copy() implementation in AttrDict using
deepcopy
- Enhance type hints and documentation for metadata classes
Chores:
- Remove unused logging imports and artifacts