audiolibrarian 0.16.2__tar.gz → 0.16.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/.github/workflows/main.yml +0 -2
  2. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/.gitignore +2 -0
  3. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/CONTRIBUTING.md +2 -2
  4. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/Makefile +4 -1
  5. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/PKG-INFO +117 -118
  6. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/README.md +115 -116
  7. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/git_hooks/README.md +0 -1
  8. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/pyproject.toml +8 -1
  9. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/__init__.py +1 -1
  10. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/picard_src/README.md +3 -3
  11. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/uv.lock +80 -0
  12. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/.python-version +0 -0
  13. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/COPYING +0 -0
  14. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/LICENSE +0 -0
  15. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/git_hooks/pre-push +0 -0
  16. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/scripts/update_links.sh +0 -0
  17. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/audiofile/__init__.py +0 -0
  18. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/audiofile/audiofile.py +0 -0
  19. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/audiofile/formats/__init__.py +0 -0
  20. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/audiofile/formats/flac.py +0 -0
  21. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/audiofile/formats/m4a.py +0 -0
  22. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/audiofile/formats/mp3.py +0 -0
  23. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/audiofile/tags.py +0 -0
  24. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/audiosource.py +0 -0
  25. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/base.py +0 -0
  26. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/cli.py +0 -0
  27. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/commands.py +0 -0
  28. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/genremanager.py +0 -0
  29. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/musicbrainz.py +0 -0
  30. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/output.py +0 -0
  31. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/records.py +0 -0
  32. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/settings.py +0 -0
  33. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/sh.py +0 -0
  34. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/audiolibrarian/text.py +0 -0
  35. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/picard_src/__init__.py +0 -0
  36. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/src/picard_src/textencoding.py +0 -0
  37. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/tools/__init__.py +0 -0
  38. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/tools/diff.py +0 -0
  39. {audiolibrarian-0.16.2 → audiolibrarian-0.16.4}/tools/show_tags.py +0 -0
@@ -1,8 +1,6 @@
1
1
  name: Makefile CI
2
2
 
3
3
  on:
4
- push:
5
- branches: [main]
6
4
  pull_request:
7
5
  branches: [main]
8
6
  release:
@@ -14,3 +14,5 @@ library/
14
14
  new_library/
15
15
  workdir/
16
16
  venv/
17
+
18
+ NOTES.md
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## Git Hooks
4
4
 
5
- The project includes a git_hooks directory with a pre-push hook that verifies that all code is
5
+ The project includes a git_hooks directory with a pre-push hook that verifies that all code is
6
6
  linted and tests pass. To have git use the hooks in this directory, run:
7
7
 
8
8
  ```bash
9
9
  git config core.hooksPath git_hooks
10
- ```
10
+ ```
@@ -4,6 +4,7 @@
4
4
  PRESET_VARS := $(.VARIABLES)
5
5
 
6
6
  # Project variables
7
+ MD_FILES := $(shell find . -name '*.md' | grep -v "/.venv/" | grep -v "/dist/")
7
8
  PROJECT_NAME := $(shell grep -e '^name =' pyproject.toml | cut -d'"' -f2)
8
9
  PY_FILES := $(shell find . -name '*.py' | grep -v "/.venv/" | grep -v "/dist/")
9
10
  PYTHON_VERSION_ := $(shell cat .python-version)
@@ -15,6 +16,7 @@ BROWSER := $(shell command -v chromium || command -v google-chrome-stable || co
15
16
  PYTHON := $(shell command -v python$(PYTHON_VERSION_))
16
17
  UV := $(shell command -v uv)
17
18
  COVERAGE := $(UV) run coverage
19
+ MDLINT := $(UV) run pymarkdownlnt
18
20
  MYPY := $(UV) run mypy
19
21
  PIP := $(UV) pip
20
22
  PYTEST := $(UV) run pytest
@@ -71,6 +73,7 @@ lint: format ## Lint the code.
71
73
  @$(RUFF) format --check src
72
74
  @$(RUFF) check src
73
75
  @$(MYPY) --non-interactive $(PY_FILES)
76
+ @$(MDLINT) scan $(MD_FILES)
74
77
 
75
78
  .PHONY: publish
76
79
  publish: $(WHEEL) ## Publish the package to PyPI.
@@ -107,7 +110,7 @@ ifndef PYTHON_VERSION
107
110
  endif
108
111
  endif
109
112
 
110
- uv.lock:
113
+ uv.lock: pyproject.toml
111
114
  @$(UV) lock
112
115
 
113
116
  $(WHEEL): $(PY_FILES) pyproject.toml uv.lock dep
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: audiolibrarian
3
- Version: 0.16.2
3
+ Version: 0.16.4
4
4
  Summary: Manage my audio library.
5
- Project-URL: Repository, https://bitbucket.org/toadstule/audiolibrarian/
5
+ Project-URL: Repository, https://github.com/toadstule/audiolibrarian
6
6
  Author-email: Steve Jibson <steve@jibson.com>
7
7
  License-File: COPYING
8
8
  License-File: LICENSE
@@ -27,8 +27,9 @@ Description-Content-Type: text/markdown
27
27
 
28
28
  ## Overview ##
29
29
 
30
- `audiolibrarian` is a powerful command-line tool for managing digital music libraries. It provides a streamlined
31
- workflow for ripping, converting, organizing, and tagging audio files with high-quality metadata from MusicBrainz.
30
+ `audiolibrarian` is a command-line tool for ripping audio from CDs (or taking
31
+ high-quality audio from local files), tagging them with comprehensive metadata from MusicBrainz,
32
+ converting them to multiple formats, and organizing them in a clean directory structure.
32
33
 
33
34
  ### Key Features ###
34
35
 
@@ -45,77 +46,78 @@ workflow for ripping, converting, organizing, and tagging audio files with high-
45
46
  - **Consistent Quality**: Maintains audio quality through the conversion process
46
47
  - **Accurate Metadata**: Leverages MusicBrainz for comprehensive music information
47
48
  - **Automated Workflow**: Reduces manual work in organizing and tagging music
48
- - **Scriptable**: Perfect for automating large music library management tasks
49
49
  - **Open Source**: Free to use and modify under the GPL-3.0 license
50
50
 
51
- Whether you're digitizing a CD collection, organizing existing music files, or managing a large digital library,
52
- `audiolibrarian` provides the tools you need to keep your music collection well-organized and properly tagged.
53
-
51
+ Whether you're digitizing a CD collection, organizing existing music files, or managing a large
52
+ digital library, `audiolibrarian` provides the tools you need to keep your music collection
53
+ well-organized and properly tagged.
54
54
 
55
55
  ## Installation ##
56
56
 
57
+ > **NOTE:** This library has only been tested on Linux. It may not work on other operating
58
+ > systems.
59
+
57
60
  ### External Requirements ###
58
61
 
59
62
  `audiolibrarian` uses a few command-line tools to run:
60
63
 
61
- * `cd-paranoia`: [cd-paranoia](https://www.gnu.org/software/libcdio/)
62
- * `eject`: [util-linux](https://github.com/util-linux/util-linux)
63
- * `faad`: [faad2](https://github.com/knik0/faad2)
64
- * `fdkaac`: [fdkaac](https://github.com/nu774/fdkaac)
65
- * `flac`: [flac](https://github.com/xiph/flac)
66
- * `lame`: [lame](https://lame.sourceforge.io/)
67
- * `mpg123`: [mpg123](https://www.mpg123.de/)
68
- * `sndfile-convert`: [libsndfile](https://github.com/libsndfile/libsndfile)
69
- * `wavegain`: [wavegain](https://rarewares.org/others.php)
70
-
71
- ### Install from PyPI ###
64
+ - [cd-paranoia](https://www.gnu.org/software/libcdio/)
65
+ - [util-linux](https://github.com/util-linux/util-linux)
66
+ - [faad2](https://github.com/knik0/faad2)
67
+ - [fdkaac](https://github.com/nu774/fdkaac)
68
+ - [flac](https://github.com/xiph/flac)
69
+ - [lame](https://lame.sourceforge.io/)
70
+ - [mpg123](https://www.mpg123.de/)
71
+ - [libsndfile](https://github.com/libsndfile/libsndfile)
72
+ - [wavegain](https://github.com/MestreLion/wavegain)
72
73
 
73
- `audiolibrarian` is not available on PyPI, but it can be installed from a local PyPI.
74
+ It also requires the [libdiscid](https://musicbrainz.org/doc/libdiscid) library.
74
75
 
75
- ```bash
76
- pip install --user --extra-index-url=https://pypi.example.com/simple audiolibrarian
77
- ```
76
+ ### Install from PyPI ###
78
77
 
79
- ### Install from Bitbucket ###
78
+ `audiolibrarian` is available on PyPI:
80
79
 
81
80
  ```bash
82
- pip install --user git+https://bitbucket.org/toadstule/audiolibrarian
81
+ pip install audiolibrarian
83
82
  ```
84
83
 
85
84
  ## Configuration ##
86
85
 
87
- `audiolibrarian` uses a flexible configuration system that supports multiple configuration sources, listed in order of precedence:
86
+ `audiolibrarian` uses a flexible configuration system that supports multiple configuration sources,
87
+ listed in order of precedence:
88
88
 
89
89
  1. **Environment Variables** (highest precedence)
90
90
  - Prefix: `AUDIOLIBRARIAN__`
91
91
  - Nested fields: Use `__` as delimiter (e.g., `AUDIOLIBRARIAN__MUSICBRAINZ__USERNAME`)
92
92
  - Example:
93
+
93
94
  ```bash
94
- # Override library directory
95
+ # Override library directory (library_dir)
95
96
  export AUDIOLIBRARIAN__LIBRARY_DIR="/mnt/music/library"
96
97
 
97
- # Set MusicBrainz credentials
98
+ # Set MusicBrainz credentials (musicbrainz.username and musicbrainz.password)
98
99
  export AUDIOLIBRARIAN__MUSICBRAINZ__USERNAME="your_username"
99
100
  export AUDIOLIBRARIAN__MUSICBRAINZ__PASSWORD="your_password"
100
101
  ```
101
102
 
102
- 2. **YAML Configuration File**
103
- - Location: `~/.config/audiolibrarian/config.yaml` (or `$XDG_CONFIG_HOME/audiolibrarian/config.yaml` if set)
103
+ 2. **YAML Configuration File** (medium precedence)
104
+ - Default location: `~/.config/audiolibrarian/config.yaml`
104
105
  - Example:
106
+
105
107
  ```yaml
106
108
  # Base directory for your music library
107
109
  library_dir: "~/music/library"
108
-
110
+
109
111
  # Cache and working directory
110
112
  work_dir: "~/.cache/audiolibrarian"
111
-
113
+
112
114
  # CD/DVD device path (use null for default device)
113
115
  discid_device: null
114
-
116
+
115
117
  # Audio normalization settings
116
118
  normalize_gain: 5 # dB gain for normalization
117
119
  normalize_preset: "radio" # "album" or "radio"
118
-
120
+
119
121
  # MusicBrainz API settings (optional)
120
122
  musicbrainz:
121
123
  username: "your_username" # For personal genre preferences
@@ -128,18 +130,23 @@ pip install --user git+https://bitbucket.org/toadstule/audiolibrarian
128
130
 
129
131
  ### Available Settings ###
130
132
 
131
- | Setting | Default | Description |
132
- |--------------------------|----------------------------------|-------------------------------------------|
133
- | `library_dir` | `library` (in the current dir) | Directory for storing audio files |
134
- | `work_dir` | `$XDG_CACHE_HOME/audiolibrarian` | Directory for temporary files |
135
- | `discid_device` | `null` | CD device path (null for default device) |
136
- | `normalize_gain` | `5` | Normalization gain in dB |
137
- | `normalize_preset` | `"radio"` | Normalization preset ("album" or "radio") |
138
- | `musicbrainz.username` | (not set) | MusicBrainz username |
139
- | `musicbrainz.password` | (not set) | MusicBrainz password |
140
- | `musicbrainz.rate_limit` | `1.5` | Seconds between requests |
141
-
142
- > **Note**: The `musicbrainz` section is optional but recommended for accessing personal genre preferences on [MusicBrainz](https://musicbrainz.org/).
133
+ | Setting | Default | Description |
134
+ |--------------------------|---------------------------|-------------------------------------------|
135
+ | `library_dir` | `./library` | Directory for storing audio files |
136
+ | `work_dir` | `~/.cache/audiolibrarian` | Directory for temporary files |
137
+ | `discid_device` | `null` | CD device path (null for default device) |
138
+ | `normalize_gain` | `5` | Normalization gain in dB |
139
+ | `normalize_preset` | `"radio"` | Normalization preset ("album" or "radio") |
140
+ | `musicbrainz.username` | (not set) | MusicBrainz username |
141
+ | `musicbrainz.password` | (not set) | MusicBrainz password |
142
+ | `musicbrainz.rate_limit` | `1.5` | Seconds between requests |
143
+
144
+ > **Notes**:
145
+ >
146
+ > - The `musicbrainz` username and password are optional but recommended for accessing personal genre
147
+ > preferences on [MusicBrainz](https://musicbrainz.org/).
148
+ > - The `work_dir` default is actually `$XDG_CACHE_HOME/audiolibrarian`, which defaults to
149
+ > `~/.cache/audiolibrarian` on Linux and macOS.
143
150
 
144
151
  ## Usage ##
145
152
 
@@ -168,31 +175,40 @@ audiolibrarian genre /path/to/audio/directories --tag # Update tags with MB gen
168
175
  audiolibrarian --help
169
176
  ```
170
177
 
171
- ### Combining Configuration Sources ###
172
-
173
- Configuration sources are combined with the following precedence (highest to lowest):
174
- 1. Environment variables
175
- 2. YAML configuration file
176
- 3. Default values
177
-
178
- For example, with this `config.yaml`:
178
+ ### Directory Structure ###
179
179
 
180
- ```yaml
181
- # config.yaml
182
- library_dir: /media/music/library
183
- normalize_gain: 5.0
184
- ```
180
+ `audiolibrarian` organizes files in the following structure:
185
181
 
186
- And this environment variable:
187
- ```bash
188
- export AUDIOLIBRARIAN__NORMALIZE_GAIN="8.0"
189
- ```
182
+ **Processed audio files** (organized by format):
190
183
 
191
- The effective value of `normalize_gain` will be `8.0` (from the environment variable), while `library_dir` will be set to `/media/music/library` from the YAML file.
184
+ ```text
185
+ library/
186
+ ├── flac/
187
+ │ └── Artist/
188
+ │ └── YYYY__Album/
189
+ │ ├── 01__Track_Title.flac
190
+ │ └── 02__Another_Track.flac
191
+ ├── m4a/
192
+ │ └── Artist/
193
+ │ └── YYYY__Album/
194
+ │ ├── 01__Track_Title.m4a
195
+ │ └── 02__Another_Track.m4a
196
+ ├── mp3/
197
+ │ └── Artist/
198
+ │ └── YYYY__Album/
199
+ │ ├── 01__Track_Title.mp3
200
+ │ └── 02__Another_Track.mp3
201
+ ├── source/
202
+ │ └── Artist/
203
+ │ └── YYYY__Album/
204
+ │ ├── 01__Track_Title.flac
205
+ │ ├── 02__Another_Track.flac
206
+ │ └── Manifest.yaml
207
+ ```
192
208
 
193
209
  ### Advanced Usage ###
194
210
 
195
- 1. **Ripping CDs**
211
+ #### Ripping CDs ####
196
212
 
197
213
  ```bash
198
214
  # Basic CD rip
@@ -208,7 +224,7 @@ audiolibrarian rip --mb-release-id "12345678-1234-1234-1234-123456789012"
208
224
  audiolibrarian rip --disc "1/2" # First disc of two
209
225
  ```
210
226
 
211
- 2. **Converting Audio Files**
227
+ #### Converting Audio Files ####
212
228
 
213
229
  ```bash
214
230
  # Convert with specific artist and album
@@ -221,7 +237,7 @@ audiolibrarian convert --mb-release-id "12345678-1234-1234-1234-123456789012" /p
221
237
  audiolibrarian convert --disc "1/2" /path/to/disc1/files
222
238
  ```
223
239
 
224
- 2. **Working with Manifests**
240
+ #### Working with Manifests ####
225
241
 
226
242
  ```bash
227
243
  # Create manifest for existing files
@@ -231,11 +247,13 @@ audiolibrarian manifest /path/to/audio/files
231
247
  audiolibrarian manifest --cd /path/to/audio/files
232
248
 
233
249
  # Specify MusicBrainz artist and release IDs
234
- audiolibrarian manifest --mb-artist-id "12345678-1234-1234-1234-123456789012" \
235
- --mb-release-id "12345678-1234-1234-1234-123456789012" /path/to/audio/files
250
+ audiolibrarian manifest \
251
+ --mb-artist-id "12345678-1234-1234-1234-123456789012" \
252
+ --mb-release-id "87654321-4321-4321-4321-210987654321" \
253
+ /path/to/audio/files
236
254
  ```
237
255
 
238
- 3. **Reconverting Files**
256
+ #### Reconverting Files ####
239
257
 
240
258
  ```bash
241
259
  # Reconvert all files in directory
@@ -245,7 +263,7 @@ audiolibrarian reconvert /path/to/source/directories
245
263
  audiolibrarian reconvert --dry-run /path/to/source/directories
246
264
  ```
247
265
 
248
- 4. **Renaming Files**
266
+ #### Renaming Files ####
249
267
 
250
268
  ```bash
251
269
  # Rename files based on tags
@@ -255,7 +273,7 @@ audiolibrarian rename /path/to/audio/directories
255
273
  audiolibrarian rename --dry-run /path/to/audio/directories
256
274
  ```
257
275
 
258
- 5. **Using Different Normalization Presets**
276
+ #### Using Different Normalization Presets ####
259
277
 
260
278
  ```bash
261
279
  # Use radio normalization preset (default)
@@ -265,9 +283,33 @@ export AUDIOLIBRARIAN__NORMALIZE_PRESET="radio"
265
283
  export AUDIOLIBRARIAN__NORMALIZE_PRESET="album"
266
284
  ```
267
285
 
286
+ #### Combining Configuration Sources ####
287
+
288
+ Configuration sources are combined with the following precedence (highest to lowest):
289
+ 1. Environment variables
290
+ 2. YAML configuration file
291
+ 3. Default values
292
+
293
+ For example, with this `config.yaml`:
294
+
295
+ ```yaml
296
+ # config.yaml
297
+ library_dir: /media/music/library
298
+ normalize_gain: 5.0
299
+ ```
300
+
301
+ And this environment variable:
302
+
303
+ ```bash
304
+ export AUDIOLIBRARIAN__NORMALIZE_GAIN="8.0"
305
+ ```
306
+
307
+ The effective value of `normalize_gain` will be `8.0` (from the environment variable), while
308
+ `library_dir` will be set to `/media/music/library` from the YAML file.
309
+
268
310
  ### Troubleshooting ###
269
311
 
270
- 1. **Increasing Verbosity**
312
+ #### Increasing Verbosity ####
271
313
 
272
314
  ```bash
273
315
  # Show more detailed output
@@ -277,58 +319,15 @@ audiolibrarian --log-level INFO cd
277
319
  audiolibrarian --log-level DEBUG cd
278
320
  ```
279
321
 
280
- 2. **Checking Dependencies**
281
-
282
- ```bash
283
- # Verify all required tools are installed
284
- audiolibrarian --log-level DEBUG cd
285
- ```
286
-
287
- 3. **MusicBrainz Issues**
322
+ #### MusicBrainz Issues ####
288
323
 
289
324
  If you encounter MusicBrainz-related errors:
290
325
 
291
326
  1. Verify your credentials are correct
292
- 2. Check your internet connection
327
+ 2. Check your Internet connection
293
328
  3. Use the debug log level to get more information
294
329
  4. Increase the rate limit if you're hitting rate limits
295
330
 
296
331
  ```bash
297
332
  export AUDIOLIBRARIAN__MUSICBRAINZ__RATE_LIMIT="2.0"
298
333
  ```
299
-
300
- ### Directory Structure ###
301
-
302
- `audiolibrarian` organizes files in the following structure:
303
-
304
- 1. **Source files** (original audio files):
305
- ```
306
- library/source/
307
- └── Artist/
308
- └── YYYY__Album/
309
- ├── 01__Track_Title.flac
310
- ├── 02__Another_Track.flac
311
- └── Manifest.yaml
312
- ```
313
-
314
- 2. **Processed audio files** (organized by format):
315
- ```
316
- library/
317
- ├── flac/
318
- │ └── Artist/
319
- │ └── YYYY__Album/
320
- │ ├── 01__Track_Title.flac
321
- │ └── 02__Another_Track.flac
322
- ├── m4a/
323
- │ └── Artist/
324
- │ └── YYYY__Album/
325
- │ ├── 01__Track_Title.m4a
326
- │ └── 02__Another_Track.m4a
327
- └── mp3/
328
- └── Artist/
329
- └── YYYY__Album/
330
- ├── 01__Track_Title.mp3
331
- └── 02__Another_Track.mp3
332
- ```
333
-
334
- Each track filename follows the format: `track_number__track_name.extension` (e.g., `01__Call_to_Arms.flac`).
@@ -2,8 +2,9 @@
2
2
 
3
3
  ## Overview ##
4
4
 
5
- `audiolibrarian` is a powerful command-line tool for managing digital music libraries. It provides a streamlined
6
- workflow for ripping, converting, organizing, and tagging audio files with high-quality metadata from MusicBrainz.
5
+ `audiolibrarian` is a command-line tool for ripping audio from CDs (or taking
6
+ high-quality audio from local files), tagging them with comprehensive metadata from MusicBrainz,
7
+ converting them to multiple formats, and organizing them in a clean directory structure.
7
8
 
8
9
  ### Key Features ###
9
10
 
@@ -20,77 +21,78 @@ workflow for ripping, converting, organizing, and tagging audio files with high-
20
21
  - **Consistent Quality**: Maintains audio quality through the conversion process
21
22
  - **Accurate Metadata**: Leverages MusicBrainz for comprehensive music information
22
23
  - **Automated Workflow**: Reduces manual work in organizing and tagging music
23
- - **Scriptable**: Perfect for automating large music library management tasks
24
24
  - **Open Source**: Free to use and modify under the GPL-3.0 license
25
25
 
26
- Whether you're digitizing a CD collection, organizing existing music files, or managing a large digital library,
27
- `audiolibrarian` provides the tools you need to keep your music collection well-organized and properly tagged.
28
-
26
+ Whether you're digitizing a CD collection, organizing existing music files, or managing a large
27
+ digital library, `audiolibrarian` provides the tools you need to keep your music collection
28
+ well-organized and properly tagged.
29
29
 
30
30
  ## Installation ##
31
31
 
32
+ > **NOTE:** This library has only been tested on Linux. It may not work on other operating
33
+ > systems.
34
+
32
35
  ### External Requirements ###
33
36
 
34
37
  `audiolibrarian` uses a few command-line tools to run:
35
38
 
36
- * `cd-paranoia`: [cd-paranoia](https://www.gnu.org/software/libcdio/)
37
- * `eject`: [util-linux](https://github.com/util-linux/util-linux)
38
- * `faad`: [faad2](https://github.com/knik0/faad2)
39
- * `fdkaac`: [fdkaac](https://github.com/nu774/fdkaac)
40
- * `flac`: [flac](https://github.com/xiph/flac)
41
- * `lame`: [lame](https://lame.sourceforge.io/)
42
- * `mpg123`: [mpg123](https://www.mpg123.de/)
43
- * `sndfile-convert`: [libsndfile](https://github.com/libsndfile/libsndfile)
44
- * `wavegain`: [wavegain](https://rarewares.org/others.php)
45
-
46
- ### Install from PyPI ###
39
+ - [cd-paranoia](https://www.gnu.org/software/libcdio/)
40
+ - [util-linux](https://github.com/util-linux/util-linux)
41
+ - [faad2](https://github.com/knik0/faad2)
42
+ - [fdkaac](https://github.com/nu774/fdkaac)
43
+ - [flac](https://github.com/xiph/flac)
44
+ - [lame](https://lame.sourceforge.io/)
45
+ - [mpg123](https://www.mpg123.de/)
46
+ - [libsndfile](https://github.com/libsndfile/libsndfile)
47
+ - [wavegain](https://github.com/MestreLion/wavegain)
47
48
 
48
- `audiolibrarian` is not available on PyPI, but it can be installed from a local PyPI.
49
+ It also requires the [libdiscid](https://musicbrainz.org/doc/libdiscid) library.
49
50
 
50
- ```bash
51
- pip install --user --extra-index-url=https://pypi.example.com/simple audiolibrarian
52
- ```
51
+ ### Install from PyPI ###
53
52
 
54
- ### Install from Bitbucket ###
53
+ `audiolibrarian` is available on PyPI:
55
54
 
56
55
  ```bash
57
- pip install --user git+https://bitbucket.org/toadstule/audiolibrarian
56
+ pip install audiolibrarian
58
57
  ```
59
58
 
60
59
  ## Configuration ##
61
60
 
62
- `audiolibrarian` uses a flexible configuration system that supports multiple configuration sources, listed in order of precedence:
61
+ `audiolibrarian` uses a flexible configuration system that supports multiple configuration sources,
62
+ listed in order of precedence:
63
63
 
64
64
  1. **Environment Variables** (highest precedence)
65
65
  - Prefix: `AUDIOLIBRARIAN__`
66
66
  - Nested fields: Use `__` as delimiter (e.g., `AUDIOLIBRARIAN__MUSICBRAINZ__USERNAME`)
67
67
  - Example:
68
+
68
69
  ```bash
69
- # Override library directory
70
+ # Override library directory (library_dir)
70
71
  export AUDIOLIBRARIAN__LIBRARY_DIR="/mnt/music/library"
71
72
 
72
- # Set MusicBrainz credentials
73
+ # Set MusicBrainz credentials (musicbrainz.username and musicbrainz.password)
73
74
  export AUDIOLIBRARIAN__MUSICBRAINZ__USERNAME="your_username"
74
75
  export AUDIOLIBRARIAN__MUSICBRAINZ__PASSWORD="your_password"
75
76
  ```
76
77
 
77
- 2. **YAML Configuration File**
78
- - Location: `~/.config/audiolibrarian/config.yaml` (or `$XDG_CONFIG_HOME/audiolibrarian/config.yaml` if set)
78
+ 2. **YAML Configuration File** (medium precedence)
79
+ - Default location: `~/.config/audiolibrarian/config.yaml`
79
80
  - Example:
81
+
80
82
  ```yaml
81
83
  # Base directory for your music library
82
84
  library_dir: "~/music/library"
83
-
85
+
84
86
  # Cache and working directory
85
87
  work_dir: "~/.cache/audiolibrarian"
86
-
88
+
87
89
  # CD/DVD device path (use null for default device)
88
90
  discid_device: null
89
-
91
+
90
92
  # Audio normalization settings
91
93
  normalize_gain: 5 # dB gain for normalization
92
94
  normalize_preset: "radio" # "album" or "radio"
93
-
95
+
94
96
  # MusicBrainz API settings (optional)
95
97
  musicbrainz:
96
98
  username: "your_username" # For personal genre preferences
@@ -103,18 +105,23 @@ pip install --user git+https://bitbucket.org/toadstule/audiolibrarian
103
105
 
104
106
  ### Available Settings ###
105
107
 
106
- | Setting | Default | Description |
107
- |--------------------------|----------------------------------|-------------------------------------------|
108
- | `library_dir` | `library` (in the current dir) | Directory for storing audio files |
109
- | `work_dir` | `$XDG_CACHE_HOME/audiolibrarian` | Directory for temporary files |
110
- | `discid_device` | `null` | CD device path (null for default device) |
111
- | `normalize_gain` | `5` | Normalization gain in dB |
112
- | `normalize_preset` | `"radio"` | Normalization preset ("album" or "radio") |
113
- | `musicbrainz.username` | (not set) | MusicBrainz username |
114
- | `musicbrainz.password` | (not set) | MusicBrainz password |
115
- | `musicbrainz.rate_limit` | `1.5` | Seconds between requests |
116
-
117
- > **Note**: The `musicbrainz` section is optional but recommended for accessing personal genre preferences on [MusicBrainz](https://musicbrainz.org/).
108
+ | Setting | Default | Description |
109
+ |--------------------------|---------------------------|-------------------------------------------|
110
+ | `library_dir` | `./library` | Directory for storing audio files |
111
+ | `work_dir` | `~/.cache/audiolibrarian` | Directory for temporary files |
112
+ | `discid_device` | `null` | CD device path (null for default device) |
113
+ | `normalize_gain` | `5` | Normalization gain in dB |
114
+ | `normalize_preset` | `"radio"` | Normalization preset ("album" or "radio") |
115
+ | `musicbrainz.username` | (not set) | MusicBrainz username |
116
+ | `musicbrainz.password` | (not set) | MusicBrainz password |
117
+ | `musicbrainz.rate_limit` | `1.5` | Seconds between requests |
118
+
119
+ > **Notes**:
120
+ >
121
+ > - The `musicbrainz` username and password are optional but recommended for accessing personal genre
122
+ > preferences on [MusicBrainz](https://musicbrainz.org/).
123
+ > - The `work_dir` default is actually `$XDG_CACHE_HOME/audiolibrarian`, which defaults to
124
+ > `~/.cache/audiolibrarian` on Linux and macOS.
118
125
 
119
126
  ## Usage ##
120
127
 
@@ -143,31 +150,40 @@ audiolibrarian genre /path/to/audio/directories --tag # Update tags with MB gen
143
150
  audiolibrarian --help
144
151
  ```
145
152
 
146
- ### Combining Configuration Sources ###
147
-
148
- Configuration sources are combined with the following precedence (highest to lowest):
149
- 1. Environment variables
150
- 2. YAML configuration file
151
- 3. Default values
152
-
153
- For example, with this `config.yaml`:
153
+ ### Directory Structure ###
154
154
 
155
- ```yaml
156
- # config.yaml
157
- library_dir: /media/music/library
158
- normalize_gain: 5.0
159
- ```
155
+ `audiolibrarian` organizes files in the following structure:
160
156
 
161
- And this environment variable:
162
- ```bash
163
- export AUDIOLIBRARIAN__NORMALIZE_GAIN="8.0"
164
- ```
157
+ **Processed audio files** (organized by format):
165
158
 
166
- The effective value of `normalize_gain` will be `8.0` (from the environment variable), while `library_dir` will be set to `/media/music/library` from the YAML file.
159
+ ```text
160
+ library/
161
+ ├── flac/
162
+ │ └── Artist/
163
+ │ └── YYYY__Album/
164
+ │ ├── 01__Track_Title.flac
165
+ │ └── 02__Another_Track.flac
166
+ ├── m4a/
167
+ │ └── Artist/
168
+ │ └── YYYY__Album/
169
+ │ ├── 01__Track_Title.m4a
170
+ │ └── 02__Another_Track.m4a
171
+ ├── mp3/
172
+ │ └── Artist/
173
+ │ └── YYYY__Album/
174
+ │ ├── 01__Track_Title.mp3
175
+ │ └── 02__Another_Track.mp3
176
+ ├── source/
177
+ │ └── Artist/
178
+ │ └── YYYY__Album/
179
+ │ ├── 01__Track_Title.flac
180
+ │ ├── 02__Another_Track.flac
181
+ │ └── Manifest.yaml
182
+ ```
167
183
 
168
184
  ### Advanced Usage ###
169
185
 
170
- 1. **Ripping CDs**
186
+ #### Ripping CDs ####
171
187
 
172
188
  ```bash
173
189
  # Basic CD rip
@@ -183,7 +199,7 @@ audiolibrarian rip --mb-release-id "12345678-1234-1234-1234-123456789012"
183
199
  audiolibrarian rip --disc "1/2" # First disc of two
184
200
  ```
185
201
 
186
- 2. **Converting Audio Files**
202
+ #### Converting Audio Files ####
187
203
 
188
204
  ```bash
189
205
  # Convert with specific artist and album
@@ -196,7 +212,7 @@ audiolibrarian convert --mb-release-id "12345678-1234-1234-1234-123456789012" /p
196
212
  audiolibrarian convert --disc "1/2" /path/to/disc1/files
197
213
  ```
198
214
 
199
- 2. **Working with Manifests**
215
+ #### Working with Manifests ####
200
216
 
201
217
  ```bash
202
218
  # Create manifest for existing files
@@ -206,11 +222,13 @@ audiolibrarian manifest /path/to/audio/files
206
222
  audiolibrarian manifest --cd /path/to/audio/files
207
223
 
208
224
  # Specify MusicBrainz artist and release IDs
209
- audiolibrarian manifest --mb-artist-id "12345678-1234-1234-1234-123456789012" \
210
- --mb-release-id "12345678-1234-1234-1234-123456789012" /path/to/audio/files
225
+ audiolibrarian manifest \
226
+ --mb-artist-id "12345678-1234-1234-1234-123456789012" \
227
+ --mb-release-id "87654321-4321-4321-4321-210987654321" \
228
+ /path/to/audio/files
211
229
  ```
212
230
 
213
- 3. **Reconverting Files**
231
+ #### Reconverting Files ####
214
232
 
215
233
  ```bash
216
234
  # Reconvert all files in directory
@@ -220,7 +238,7 @@ audiolibrarian reconvert /path/to/source/directories
220
238
  audiolibrarian reconvert --dry-run /path/to/source/directories
221
239
  ```
222
240
 
223
- 4. **Renaming Files**
241
+ #### Renaming Files ####
224
242
 
225
243
  ```bash
226
244
  # Rename files based on tags
@@ -230,7 +248,7 @@ audiolibrarian rename /path/to/audio/directories
230
248
  audiolibrarian rename --dry-run /path/to/audio/directories
231
249
  ```
232
250
 
233
- 5. **Using Different Normalization Presets**
251
+ #### Using Different Normalization Presets ####
234
252
 
235
253
  ```bash
236
254
  # Use radio normalization preset (default)
@@ -240,9 +258,33 @@ export AUDIOLIBRARIAN__NORMALIZE_PRESET="radio"
240
258
  export AUDIOLIBRARIAN__NORMALIZE_PRESET="album"
241
259
  ```
242
260
 
261
+ #### Combining Configuration Sources ####
262
+
263
+ Configuration sources are combined with the following precedence (highest to lowest):
264
+ 1. Environment variables
265
+ 2. YAML configuration file
266
+ 3. Default values
267
+
268
+ For example, with this `config.yaml`:
269
+
270
+ ```yaml
271
+ # config.yaml
272
+ library_dir: /media/music/library
273
+ normalize_gain: 5.0
274
+ ```
275
+
276
+ And this environment variable:
277
+
278
+ ```bash
279
+ export AUDIOLIBRARIAN__NORMALIZE_GAIN="8.0"
280
+ ```
281
+
282
+ The effective value of `normalize_gain` will be `8.0` (from the environment variable), while
283
+ `library_dir` will be set to `/media/music/library` from the YAML file.
284
+
243
285
  ### Troubleshooting ###
244
286
 
245
- 1. **Increasing Verbosity**
287
+ #### Increasing Verbosity ####
246
288
 
247
289
  ```bash
248
290
  # Show more detailed output
@@ -252,58 +294,15 @@ audiolibrarian --log-level INFO cd
252
294
  audiolibrarian --log-level DEBUG cd
253
295
  ```
254
296
 
255
- 2. **Checking Dependencies**
256
-
257
- ```bash
258
- # Verify all required tools are installed
259
- audiolibrarian --log-level DEBUG cd
260
- ```
261
-
262
- 3. **MusicBrainz Issues**
297
+ #### MusicBrainz Issues ####
263
298
 
264
299
  If you encounter MusicBrainz-related errors:
265
300
 
266
301
  1. Verify your credentials are correct
267
- 2. Check your internet connection
302
+ 2. Check your Internet connection
268
303
  3. Use the debug log level to get more information
269
304
  4. Increase the rate limit if you're hitting rate limits
270
305
 
271
306
  ```bash
272
307
  export AUDIOLIBRARIAN__MUSICBRAINZ__RATE_LIMIT="2.0"
273
308
  ```
274
-
275
- ### Directory Structure ###
276
-
277
- `audiolibrarian` organizes files in the following structure:
278
-
279
- 1. **Source files** (original audio files):
280
- ```
281
- library/source/
282
- └── Artist/
283
- └── YYYY__Album/
284
- ├── 01__Track_Title.flac
285
- ├── 02__Another_Track.flac
286
- └── Manifest.yaml
287
- ```
288
-
289
- 2. **Processed audio files** (organized by format):
290
- ```
291
- library/
292
- ├── flac/
293
- │ └── Artist/
294
- │ └── YYYY__Album/
295
- │ ├── 01__Track_Title.flac
296
- │ └── 02__Another_Track.flac
297
- ├── m4a/
298
- │ └── Artist/
299
- │ └── YYYY__Album/
300
- │ ├── 01__Track_Title.m4a
301
- │ └── 02__Another_Track.m4a
302
- └── mp3/
303
- └── Artist/
304
- └── YYYY__Album/
305
- ├── 01__Track_Title.mp3
306
- └── 02__Another_Track.mp3
307
- ```
308
-
309
- Each track filename follows the format: `track_number__track_name.extension` (e.g., `01__Call_to_Arms.flac`).
@@ -5,4 +5,3 @@ To have git use the hooks in this directory, run
5
5
  ```bash
6
6
  git config core.hooksPath git_hooks
7
7
  ```
8
-
@@ -7,6 +7,7 @@ dev = [
7
7
  "coverage",
8
8
  "hatchling",
9
9
  "mypy",
10
+ "pymarkdownlnt",
10
11
  "pytest",
11
12
  "ruff",
12
13
  "types-pyyaml",
@@ -46,7 +47,7 @@ requires-python = "==3.13.*"
46
47
  audiolibrarian = "audiolibrarian:cli.main"
47
48
 
48
49
  [project.urls]
49
- Repository = "https://bitbucket.org/toadstule/audiolibrarian/"
50
+ Repository = "https://github.com/toadstule/audiolibrarian"
50
51
 
51
52
  [tool.hatch.build.targets.sdist]
52
53
  exclude= [
@@ -77,6 +78,12 @@ strict = true
77
78
  module = "picard_src.*"
78
79
  ignore_errors = true # This is not our code.
79
80
 
81
+ [tool.pymarkdown]
82
+ plugins.md013.line_length = 100
83
+ plugins.md013.code_block_line_length = 100
84
+ #plugins.md007.enabled = true
85
+ #plugins.md007.code_block_line_length = 160
86
+
80
87
  [tool.ruff]
81
88
  extend-exclude = [
82
89
  "picard_src", # This is not our code.
@@ -16,4 +16,4 @@
16
16
  # You should have received a copy of the GNU General Public License along with audiolibrarian.
17
17
  # If not, see <https://www.gnu.org/licenses/>.
18
18
  #
19
- __version__ = "0.16.2"
19
+ __version__ = "0.16.4"
@@ -2,10 +2,10 @@
2
2
 
3
3
  Code in this directory comes from (or is derived from) the super-cool Picard project.
4
4
 
5
- https://github.com/metabrainz/picard
5
+ [Picard](https://github.com/metabrainz/picard)
6
6
 
7
7
  I'd like to express my thanks to all of have contributed to that project.
8
8
 
9
- Note: we could have just set the Picard library as a dependency of audiolibrarian, but
10
- it includes other dependencies (such as PyQT) that add a lot of overhead not required
9
+ Note: we could have just set the Picard library as a dependency of audiolibrarian, but
10
+ it includes other dependencies (such as PyQT) that add a lot of overhead not required
11
11
  by this project.
@@ -20,6 +20,20 @@ wheels = [
20
20
  { url = "https://files.pythonhosted.org/packages/53/18/a56e2fe47b259bb52201093a3a9d4a32014f9d85071ad07e9d60600890ca/ansicolors-1.1.8-py2.py3-none-any.whl", hash = "sha256:00d2dde5a675579325902536738dd27e4fac1fd68f773fe36c21044eb559e187", size = 13847, upload-time = "2017-06-02T21:22:12.67Z" },
21
21
  ]
22
22
 
23
+ [[package]]
24
+ name = "application-properties"
25
+ version = "0.8.2"
26
+ source = { registry = "https://pypi.jibson.com/simple" }
27
+ dependencies = [
28
+ { name = "pyyaml" },
29
+ { name = "tomli" },
30
+ { name = "typing-extensions" },
31
+ ]
32
+ sdist = { url = "https://files.pythonhosted.org/packages/c1/5e/29ec0fa553ee5befc6a1e47eb0c8ffea75eed941251524678700e2d3e747/application_properties-0.8.2.tar.gz", hash = "sha256:e5e6918c8e29ab57175567d51dfa39c00a1d75b3205625559bb02250f50f0420", size = 29595, upload-time = "2024-01-28T23:43:07.403Z" }
33
+ wheels = [
34
+ { url = "https://files.pythonhosted.org/packages/06/27/ea2b77232385ec1c4b5cb766ee13b5a3085ed2fa789d61374e7af36b79e1/application_properties-0.8.2-py3-none-any.whl", hash = "sha256:a4fe684e4d95fc45054d3316acf763a7b0f29342ccea02eee09de53004f0139c", size = 18399, upload-time = "2024-01-28T23:43:05.631Z" },
35
+ ]
36
+
23
37
  [[package]]
24
38
  name = "audiolibrarian"
25
39
  source = { editable = "." }
@@ -42,6 +56,7 @@ dev = [
42
56
  { name = "coverage" },
43
57
  { name = "hatchling" },
44
58
  { name = "mypy" },
59
+ { name = "pymarkdownlnt" },
45
60
  { name = "pytest" },
46
61
  { name = "ruff" },
47
62
  { name = "types-pyyaml" },
@@ -69,6 +84,7 @@ dev = [
69
84
  { name = "coverage" },
70
85
  { name = "hatchling" },
71
86
  { name = "mypy" },
87
+ { name = "pymarkdownlnt" },
72
88
  { name = "pytest" },
73
89
  { name = "ruff" },
74
90
  { name = "types-pyyaml" },
@@ -116,6 +132,19 @@ wheels = [
116
132
  { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
117
133
  ]
118
134
 
135
+ [[package]]
136
+ name = "columnar"
137
+ version = "1.4.1"
138
+ source = { registry = "https://pypi.jibson.com/simple" }
139
+ dependencies = [
140
+ { name = "toolz" },
141
+ { name = "wcwidth" },
142
+ ]
143
+ sdist = { url = "https://files.pythonhosted.org/packages/5e/0d/a0b2fd781050d29c9df64ac6df30b5f18b775724b79779f56fc5a8298fe9/Columnar-1.4.1.tar.gz", hash = "sha256:c3cb57273333b2ff9cfaafc86f09307419330c97faa88dcfe23df05e6fbb9c72", size = 11386, upload-time = "2021-12-27T21:58:56.123Z" }
144
+ wheels = [
145
+ { url = "https://files.pythonhosted.org/packages/06/00/a17a5657bf090b9dffdb310ac273c553a38f9252f60224da9fe62d9b60e9/Columnar-1.4.1-py3-none-any.whl", hash = "sha256:8efb692a7e6ca07dcc8f4ea889960421331a5dffa8e5af81f0a67ad8ea1fc798", size = 11845, upload-time = "2021-12-27T21:58:54.388Z" },
146
+ ]
147
+
119
148
  [[package]]
120
149
  name = "coverage"
121
150
  version = "7.8.2"
@@ -370,6 +399,20 @@ wheels = [
370
399
  { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" },
371
400
  ]
372
401
 
402
+ [[package]]
403
+ name = "pymarkdownlnt"
404
+ version = "0.9.30"
405
+ source = { registry = "https://pypi.jibson.com/simple" }
406
+ dependencies = [
407
+ { name = "application-properties" },
408
+ { name = "columnar" },
409
+ { name = "typing-extensions" },
410
+ ]
411
+ sdist = { url = "https://files.pythonhosted.org/packages/86/00/4156086d7f32e3058cebb6b813e93f5a227ae2590ac528b2344d2c669973/pymarkdownlnt-0.9.30.tar.gz", hash = "sha256:cf274935b128abd7f30c44314510d0f4d36965149bb9dae84f6cac9491dc1f58", size = 416281, upload-time = "2025-05-20T02:26:30.652Z" }
412
+ wheels = [
413
+ { url = "https://files.pythonhosted.org/packages/28/92/81059c9eecd973168890e6b730f852abfd310389f67d4fb6b132a3976338/pymarkdownlnt-0.9.30-py3-none-any.whl", hash = "sha256:29b881434def9d3796be4a89cb277ab9f8f4fbb558379ce7836c653c9c351d2c", size = 500322, upload-time = "2025-05-20T02:26:29.098Z" },
414
+ ]
415
+
373
416
  [[package]]
374
417
  name = "pytest"
375
418
  version = "8.4.0"
@@ -487,6 +530,34 @@ wheels = [
487
530
  { url = "https://files.pythonhosted.org/packages/ec/bf/b273dd11673fed8a6bd46032c0ea2a04b2ac9bfa9c628756a5856ba113b0/ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b", size = 10683928, upload-time = "2025-06-05T21:00:13.758Z" },
488
531
  ]
489
532
 
533
+ [[package]]
534
+ name = "tomli"
535
+ version = "2.2.1"
536
+ source = { registry = "https://pypi.jibson.com/simple" }
537
+ sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" }
538
+ wheels = [
539
+ { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" },
540
+ { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" },
541
+ { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" },
542
+ { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" },
543
+ { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" },
544
+ { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" },
545
+ { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" },
546
+ { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" },
547
+ { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" },
548
+ { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" },
549
+ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
550
+ ]
551
+
552
+ [[package]]
553
+ name = "toolz"
554
+ version = "1.0.0"
555
+ source = { registry = "https://pypi.jibson.com/simple" }
556
+ sdist = { url = "https://files.pythonhosted.org/packages/8a/0b/d80dfa675bf592f636d1ea0b835eab4ec8df6e9415d8cfd766df54456123/toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02", size = 66790, upload-time = "2024-10-04T16:17:04.001Z" }
557
+ wheels = [
558
+ { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383, upload-time = "2024-10-04T16:17:01.533Z" },
559
+ ]
560
+
490
561
  [[package]]
491
562
  name = "trove-classifiers"
492
563
  version = "2025.5.9.12"
@@ -556,6 +627,15 @@ wheels = [
556
627
  { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" },
557
628
  ]
558
629
 
630
+ [[package]]
631
+ name = "wcwidth"
632
+ version = "0.2.13"
633
+ source = { registry = "https://pypi.jibson.com/simple" }
634
+ sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" }
635
+ wheels = [
636
+ { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" },
637
+ ]
638
+
559
639
  [[package]]
560
640
  name = "xdg-base-dirs"
561
641
  version = "6.0.2"
File without changes
File without changes