rekordbox-edit 0.6.0.dev29__tar.gz → 0.6.0.dev40__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.
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.gitignore +3 -0
- rekordbox_edit-0.6.0.dev40/.readthedocs.yaml +22 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/CHANGELOG.md +14 -3
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/Makefile +7 -1
- rekordbox_edit-0.6.0.dev40/PKG-INFO +109 -0
- rekordbox_edit-0.6.0.dev40/README.md +89 -0
- rekordbox_edit-0.6.0.dev40/docs/api.md +28 -0
- rekordbox_edit-0.6.0.dev40/docs/commands/convert.md +46 -0
- rekordbox_edit-0.6.0.dev40/docs/commands/edit.md +48 -0
- rekordbox_edit-0.6.0.dev40/docs/commands/search.md +29 -0
- rekordbox_edit-0.6.0.dev40/docs/filtering.md +108 -0
- rekordbox_edit-0.6.0.dev40/docs/index.md +1 -0
- rekordbox_edit-0.6.0.dev40/mkdocs.yml +54 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/pyproject.toml +7 -1
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/_click.py +12 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/cli/_utils.py +14 -1
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/cli/convert.py +2 -1
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/cli/edit.py +2 -1
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/cli/search.py +2 -1
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/models.py +9 -1
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/query.py +26 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/cli/test_search.py +48 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/cli/test_utils.py +16 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/test_models.py +28 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/test_query.py +49 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/uv.lock +303 -1
- rekordbox_edit-0.6.0.dev29/PKG-INFO +0 -262
- rekordbox_edit-0.6.0.dev29/README.md +0 -242
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.agent-style/RULES.md +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.agent-style/claude-code.md +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/actions/build-release-notes/action.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/actions/commitizen-bump/action.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/actions/commitizen-bump/commitizen-bump.sh +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/actions/e2e/action.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/actions/install/action.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/actions/lint/action.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/actions/test/action.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/workflows/cd.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/workflows/ci.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/workflows/publish.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.github/workflows/release.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.pre-commit-config.yaml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/.python-version +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/AGENTS.md +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/CLAUDE.md +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/CONTRIBUTING.md +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/LICENSE +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/codecov.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/docker-compose.yml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/__init__.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/api/__init__.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/api/_utils.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/api/convert.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/api/edit.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/api/search.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/cli/__init__.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/cli/main.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/display.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/logger.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/rekordbox_edit/utils.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/renovate.json5 +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/ruff.toml +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/__init__.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/api/__init__.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/api/test_convert.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/api/test_edit.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/api/test_search.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/api/test_utils.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/cli/__init__.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/cli/test_convert.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/cli/test_edit.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/cli/test_main.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/conftest.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/Dockerfile +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/__init__.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/__snapshots__/test_journey/test_search_full_json_snapshot[macos].json +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/__snapshots__/test_journey/test_search_full_json_snapshot[windows].json +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/__snapshots__/test_journey.ambr +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/conftest.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/01-flac-44_1k-16b.flac +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/02-flac-96k-24b.flac +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/03-alac-44_1k-16b.m4a +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/04-alac-48k-24b.m4a +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/05-aiff-44_1k-16b.aiff +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/06-wav-96k-24b.wav +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/07-mp3-44_1k-320cbr.mp3 +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/08-mp3-44_1k-v0vbr.mp3 +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/09-aac-44_1k-256kbps.m4a +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/audio/10-/303/274/303/261/303/256c/303/266d/303/251-flac-44_1k-16b.flac" +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/macos/master.6.8.6.db +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/fixtures/windows/master.6.8.6.db +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/e2e/test_journey.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/test_display.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/test_logger.py +0 -0
- {rekordbox_edit-0.6.0.dev29 → rekordbox_edit-0.6.0.dev40}/tests/test_utils.py +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Read the Docs configuration file
|
|
2
|
+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
3
|
+
|
|
4
|
+
version: 2
|
|
5
|
+
|
|
6
|
+
build:
|
|
7
|
+
os: ubuntu-24.04
|
|
8
|
+
tools:
|
|
9
|
+
python: "3.13"
|
|
10
|
+
jobs:
|
|
11
|
+
create_environment:
|
|
12
|
+
- asdf plugin add uv
|
|
13
|
+
- asdf install uv latest
|
|
14
|
+
- asdf global uv latest
|
|
15
|
+
install:
|
|
16
|
+
- uv sync --frozen --group docs
|
|
17
|
+
build:
|
|
18
|
+
html:
|
|
19
|
+
- uv run --group docs mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html
|
|
20
|
+
|
|
21
|
+
mkdocs:
|
|
22
|
+
configuration: mkdocs.yml
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
## v0.6.0.
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
## v0.6.0.dev40 (2026-06-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
- docs: add api reference page
|
|
5
|
+
- docs: add convert command page
|
|
6
|
+
- docs: add edit command page
|
|
7
|
+
- docs: add search command page
|
|
8
|
+
- docs: add filtering page
|
|
9
|
+
- docs: configure read the docs build
|
|
10
|
+
- docs: scaffold mkdocs material site
|
|
11
|
+
- docs: trim README to overview and quick start
|
|
12
|
+
- chore: add docs dependency group
|
|
13
|
+
- feat: add --last filter to return only the last N results
|
|
14
|
+
- feat: add --first filter to return only the first N results
|
|
4
15
|
- test(e2e): update windows snapshot with unicode representation
|
|
5
16
|
- test(e2e): force utf-8 encoding of stdout
|
|
6
17
|
- test(e2e): align TZ to UTC
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.PHONY: test test-e2e test-e2e-docker test-e2e-snapshot-update coverage lint format typecheck install-hooks run-hooks
|
|
1
|
+
.PHONY: test test-e2e test-e2e-docker test-e2e-snapshot-update coverage lint format typecheck install-hooks run-hooks docs docs-build
|
|
2
2
|
|
|
3
3
|
test:
|
|
4
4
|
uv run pytest tests
|
|
@@ -34,3 +34,9 @@ install-hooks:
|
|
|
34
34
|
|
|
35
35
|
run-hooks:
|
|
36
36
|
uv run pre-commit run --all-files
|
|
37
|
+
|
|
38
|
+
docs:
|
|
39
|
+
uv run --group docs mkdocs serve
|
|
40
|
+
|
|
41
|
+
docs-build:
|
|
42
|
+
uv run --group docs mkdocs build --strict
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rekordbox-edit
|
|
3
|
+
Version: 0.6.0.dev40
|
|
4
|
+
Summary: Tools for managing and modifying a RekordBox library en-masse
|
|
5
|
+
Project-URL: Homepage, https://github.com/jviall/rekordbox-edit
|
|
6
|
+
Project-URL: Repository, https://github.com/jviall/rekordbox-edit
|
|
7
|
+
Project-URL: Issues, https://github.com/jviall/rekordbox-edit/issues
|
|
8
|
+
Author-email: James Viall <jamesviall@pm.me>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: bulk,convert,database,dj,edit,music,rekordbox
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Requires-Dist: click<=9.0.0,>=8.0.0
|
|
14
|
+
Requires-Dist: ffmpeg-python>=0.2.0
|
|
15
|
+
Requires-Dist: platformdirs<5.0.0,>=4.3.8
|
|
16
|
+
Requires-Dist: pydantic<3,>=2.0
|
|
17
|
+
Requires-Dist: pyrekordbox==0.4.4
|
|
18
|
+
Requires-Dist: rich<15.1.0,>=15.0.0
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# rekordbox-edit
|
|
22
|
+
|
|
23
|
+
[](https://github.com/jviall/rekordbox-edit/blob/main/.github/workflows/cd.yml)
|
|
24
|
+
[](https://codecov.io/gh/jviall/rekordbox-edit)
|
|
25
|
+
[](https://pypi.org/project/rekordbox-edit/)
|
|
26
|
+
[](https://pypi.org/project/rekordbox-edit/)
|
|
27
|
+
[](https://github.com/jviall/rekordbox-edit/blob/main/LICENSE)
|
|
28
|
+
|
|
29
|
+
A command-line tool for bulk operations on your Rekordbox library. Search tracks, edit metadata, and convert audio formats — updating your database while preserving all your cues, analysis, and metadata.
|
|
30
|
+
|
|
31
|
+
> [!CAUTION]
|
|
32
|
+
> This tool can modify your Rekordbox database and audio files. Always back up your data first.
|
|
33
|
+
> No warranty is provided--you assume all risk and liability of data loss in using this.
|
|
34
|
+
> See [Safety and Best Practices](#safety-and-best-practices)
|
|
35
|
+
|
|
36
|
+
**Full documentation: [rekordbox-edit.readthedocs.io](https://rekordbox-edit.readthedocs.io/)**
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install rekordbox-edit
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Requirements:**
|
|
45
|
+
|
|
46
|
+
- Python 3.11+
|
|
47
|
+
- FFmpeg (for audio conversion)
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
Search your library:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
rbe search --artist "Daft Punk" --format flac
|
|
55
|
+
rbe search --playlist "House Favorites"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Edit track metadata:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Fix a typo across every matching title (previews and confirms first)
|
|
62
|
+
rbe edit --title "Teh" Title --match "Teh" --replace "The" --multi
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Convert audio files:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Preview what would be converted
|
|
69
|
+
rbe convert --artist "Daft Punk" --dry-run
|
|
70
|
+
|
|
71
|
+
# Convert all FLAC or WAV files to AIFF (default output format)
|
|
72
|
+
rbe convert --format flac --format wav --yes
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
See the [documentation](https://rekordbox-edit.readthedocs.io/) for every command, the full filtering language, and scripting recipes.
|
|
76
|
+
|
|
77
|
+
## Safety and Best Practices
|
|
78
|
+
|
|
79
|
+
**Before using this tool:**
|
|
80
|
+
|
|
81
|
+
1. **Back up your Rekordbox database**
|
|
82
|
+
|
|
83
|
+
Rekordbox already keeps multiple backups. But every time you close it, it creates a fresh one and deletes the oldest, so repetitive exits will quickly make those backups fairly useless.
|
|
84
|
+
|
|
85
|
+
You should make manual copies of RB's database backups before using this. You can usually find them in `~/Library/Pioneer/rekordbox/` on macOS or `%APPDATA%\Pioneer\rekordbox\` on Windows.
|
|
86
|
+
|
|
87
|
+
2. **Back up your music library**
|
|
88
|
+
|
|
89
|
+
If you don't have a back up already it's a very worthwhile investment, even if you don't plan to use this tool! Find yourself a cheap external drive, you won't regret it.
|
|
90
|
+
|
|
91
|
+
And generally limit the potential impact of a mistake by using filters to target a few tracks at a time e.g. `--artist "Crazy Frog" --first 5` before targeting a larger set, and always run with `--dry-run` first.
|
|
92
|
+
|
|
93
|
+
## AI Usage
|
|
94
|
+
|
|
95
|
+
I believe it's important to be aware of and to disclose AI usage. In many ways it's being forced upon us without us having much choice in the matter, and it's a gross and oppressive experience. While it has lots of potential to benefit the common good, mostly it's only furthered capitalist greed.
|
|
96
|
+
|
|
97
|
+
I'm mostly attempting to thoughtfully disclose that generative AI _has_ been a significant tool in building out this project. I don't personally enjoy too much coding in my personal time, but I feel passionate about making `rekordbox-edit`--AI has admittedly helped me bridge that gap between my capacity and my vision. I'm a career professional software engineer who takes pride in their work, and I don't want to produce a vibe coded mess any more than you want to experience it. Please validate the quality of this project yourself--at the end of the day it's just code written by some stranger.
|
|
98
|
+
|
|
99
|
+
If it's any consolation, my main test subject has been my own 10,000+ track RekordBox library--a risk I do not take lightly!
|
|
100
|
+
|
|
101
|
+
## Credits
|
|
102
|
+
|
|
103
|
+
This project exists thanks to [@dylanjones](https://github.com/dylanjones), the creator of [pyrekordbox](https://github.com/dylanljones/pyrekordbox), which provides the Python API for Rekordbox databases.
|
|
104
|
+
|
|
105
|
+
I built this tool to help correct my own bad habits and missteps in managing and organizing my rekordbox library. If it helps you too, great! If you find issues or have ideas, contributions are welcome.
|
|
106
|
+
|
|
107
|
+
## Contributing
|
|
108
|
+
|
|
109
|
+
See [CONTRIBUTING.md](https://github.com/jviall/rekordbox-edit/blob/main/CONTRIBUTING.md) for development setup, testing, and contribution guidelines.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# rekordbox-edit
|
|
2
|
+
|
|
3
|
+
[](https://github.com/jviall/rekordbox-edit/blob/main/.github/workflows/cd.yml)
|
|
4
|
+
[](https://codecov.io/gh/jviall/rekordbox-edit)
|
|
5
|
+
[](https://pypi.org/project/rekordbox-edit/)
|
|
6
|
+
[](https://pypi.org/project/rekordbox-edit/)
|
|
7
|
+
[](https://github.com/jviall/rekordbox-edit/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
A command-line tool for bulk operations on your Rekordbox library. Search tracks, edit metadata, and convert audio formats — updating your database while preserving all your cues, analysis, and metadata.
|
|
10
|
+
|
|
11
|
+
> [!CAUTION]
|
|
12
|
+
> This tool can modify your Rekordbox database and audio files. Always back up your data first.
|
|
13
|
+
> No warranty is provided--you assume all risk and liability of data loss in using this.
|
|
14
|
+
> See [Safety and Best Practices](#safety-and-best-practices)
|
|
15
|
+
|
|
16
|
+
**Full documentation: [rekordbox-edit.readthedocs.io](https://rekordbox-edit.readthedocs.io/)**
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install rekordbox-edit
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Requirements:**
|
|
25
|
+
|
|
26
|
+
- Python 3.11+
|
|
27
|
+
- FFmpeg (for audio conversion)
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
Search your library:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
rbe search --artist "Daft Punk" --format flac
|
|
35
|
+
rbe search --playlist "House Favorites"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Edit track metadata:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Fix a typo across every matching title (previews and confirms first)
|
|
42
|
+
rbe edit --title "Teh" Title --match "Teh" --replace "The" --multi
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Convert audio files:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Preview what would be converted
|
|
49
|
+
rbe convert --artist "Daft Punk" --dry-run
|
|
50
|
+
|
|
51
|
+
# Convert all FLAC or WAV files to AIFF (default output format)
|
|
52
|
+
rbe convert --format flac --format wav --yes
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
See the [documentation](https://rekordbox-edit.readthedocs.io/) for every command, the full filtering language, and scripting recipes.
|
|
56
|
+
|
|
57
|
+
## Safety and Best Practices
|
|
58
|
+
|
|
59
|
+
**Before using this tool:**
|
|
60
|
+
|
|
61
|
+
1. **Back up your Rekordbox database**
|
|
62
|
+
|
|
63
|
+
Rekordbox already keeps multiple backups. But every time you close it, it creates a fresh one and deletes the oldest, so repetitive exits will quickly make those backups fairly useless.
|
|
64
|
+
|
|
65
|
+
You should make manual copies of RB's database backups before using this. You can usually find them in `~/Library/Pioneer/rekordbox/` on macOS or `%APPDATA%\Pioneer\rekordbox\` on Windows.
|
|
66
|
+
|
|
67
|
+
2. **Back up your music library**
|
|
68
|
+
|
|
69
|
+
If you don't have a back up already it's a very worthwhile investment, even if you don't plan to use this tool! Find yourself a cheap external drive, you won't regret it.
|
|
70
|
+
|
|
71
|
+
And generally limit the potential impact of a mistake by using filters to target a few tracks at a time e.g. `--artist "Crazy Frog" --first 5` before targeting a larger set, and always run with `--dry-run` first.
|
|
72
|
+
|
|
73
|
+
## AI Usage
|
|
74
|
+
|
|
75
|
+
I believe it's important to be aware of and to disclose AI usage. In many ways it's being forced upon us without us having much choice in the matter, and it's a gross and oppressive experience. While it has lots of potential to benefit the common good, mostly it's only furthered capitalist greed.
|
|
76
|
+
|
|
77
|
+
I'm mostly attempting to thoughtfully disclose that generative AI _has_ been a significant tool in building out this project. I don't personally enjoy too much coding in my personal time, but I feel passionate about making `rekordbox-edit`--AI has admittedly helped me bridge that gap between my capacity and my vision. I'm a career professional software engineer who takes pride in their work, and I don't want to produce a vibe coded mess any more than you want to experience it. Please validate the quality of this project yourself--at the end of the day it's just code written by some stranger.
|
|
78
|
+
|
|
79
|
+
If it's any consolation, my main test subject has been my own 10,000+ track RekordBox library--a risk I do not take lightly!
|
|
80
|
+
|
|
81
|
+
## Credits
|
|
82
|
+
|
|
83
|
+
This project exists thanks to [@dylanjones](https://github.com/dylanjones), the creator of [pyrekordbox](https://github.com/dylanljones/pyrekordbox), which provides the Python API for Rekordbox databases.
|
|
84
|
+
|
|
85
|
+
I built this tool to help correct my own bad habits and missteps in managing and organizing my rekordbox library. If it helps you too, great! If you find issues or have ideas, contributions are welcome.
|
|
86
|
+
|
|
87
|
+
## Contributing
|
|
88
|
+
|
|
89
|
+
See [CONTRIBUTING.md](https://github.com/jviall/rekordbox-edit/blob/main/CONTRIBUTING.md) for development setup, testing, and contribution guidelines.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Everything the CLI does is available from Python. The public surface is the three functions in `rekordbox_edit.api` and the Pydantic models in `rekordbox_edit.models` that describe their inputs and outputs.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from pyrekordbox import Rekordbox6Database
|
|
7
|
+
from rekordbox_edit.api import search
|
|
8
|
+
from rekordbox_edit.models import SearchArgs
|
|
9
|
+
|
|
10
|
+
db = Rekordbox6Database()
|
|
11
|
+
response = search(db, SearchArgs(artist=["Daft Punk"], format=["flac"], match_all=True))
|
|
12
|
+
for track in response.tracks:
|
|
13
|
+
print(track.ID, track.Title)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`--print json` on any CLI command emits exactly these response envelopes, so the models below also document the JSON you get when scripting.
|
|
17
|
+
|
|
18
|
+
## Functions
|
|
19
|
+
|
|
20
|
+
::: rekordbox_edit.api.search
|
|
21
|
+
|
|
22
|
+
::: rekordbox_edit.api.edit
|
|
23
|
+
|
|
24
|
+
::: rekordbox_edit.api.convert
|
|
25
|
+
|
|
26
|
+
## Models
|
|
27
|
+
|
|
28
|
+
::: rekordbox_edit.models
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# convert
|
|
2
|
+
|
|
3
|
+
Convert audio files between formats and update the Rekordbox database to point at the new files. Your cues, analysis, beatgrids, and all metadata are preserved.
|
|
4
|
+
|
|
5
|
+
## Supported Formats
|
|
6
|
+
|
|
7
|
+
- **Input:** FLAC, AIFF, WAV (lossless only — lossy sources are skipped)
|
|
8
|
+
- **Output:** AIFF (default), FLAC, WAV, ALAC, or MP3 (320kbps CBR)
|
|
9
|
+
|
|
10
|
+
Tracks already in the target format are skipped, as are tracks whose output file already exists (override with `--overwrite`).
|
|
11
|
+
|
|
12
|
+
## Originals: Delete or Keep
|
|
13
|
+
|
|
14
|
+
After a successful conversion the original file is **deleted** for lossless output (you can always convert back) and **kept** for MP3 output (the quality loss is one-way). Override either default with `--delete` or `--keep`.
|
|
15
|
+
|
|
16
|
+
## Examples
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Preview conversion
|
|
20
|
+
rbe convert --format-out aiff --format flac --dry-run
|
|
21
|
+
|
|
22
|
+
# Convert and skip confirmation
|
|
23
|
+
rbe convert --format-out wav --artist "Burial" --yes
|
|
24
|
+
|
|
25
|
+
# Convert to MP3 but delete originals
|
|
26
|
+
rbe convert --format-out mp3 --playlist "Export" --yes --delete
|
|
27
|
+
|
|
28
|
+
# Keep originals when converting to AIFF
|
|
29
|
+
rbe convert --format-out aiff --format flac --yes --keep
|
|
30
|
+
|
|
31
|
+
# Get just the IDs of files that would be converted
|
|
32
|
+
rbe convert --format-out aiff --format flac --print ids --dry-run
|
|
33
|
+
|
|
34
|
+
# Convert everything a search finds
|
|
35
|
+
rbe search --artist "Lauryn Hill" --print ids | rbe convert --yes
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`--interactive` confirms each file individually; like `edit`, converting while Rekordbox is open triggers a warning (or a refusal in scripting modes). See [Filtering](../filtering.md) for the full filter language.
|
|
39
|
+
|
|
40
|
+
## Reference
|
|
41
|
+
|
|
42
|
+
::: mkdocs-click
|
|
43
|
+
:module: rekordbox_edit.cli.convert
|
|
44
|
+
:command: convert_command
|
|
45
|
+
:prog_name: rbe convert
|
|
46
|
+
:depth: 1
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# edit
|
|
2
|
+
|
|
3
|
+
Bulk-edit a metadata field on tracks in your Rekordbox database.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
rbe edit [OPTIONS] [TRACK-IDS]... FIELD
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
`FIELD` names the metadata field to change. Currently `Title` is the only editable field; more are planned.
|
|
10
|
+
|
|
11
|
+
`--replace` supplies the new value. On its own it overwrites the whole field; add `--match PATTERN` to find that literal text within the field and replace only that portion:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Rename one track outright
|
|
15
|
+
rbe edit --exact-title "Untitled 3" Title --replace "Acid Rain"
|
|
16
|
+
|
|
17
|
+
# Fix a typo across many titles (substring replacement)
|
|
18
|
+
rbe edit --title "Teh" Title --match "Teh" --replace "The" --multi
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Safety Rails
|
|
22
|
+
|
|
23
|
+
- **Preview and confirm by default.** Without flags, `edit` shows every planned change and asks once before applying. `--interactive` confirms each track individually; `--dry-run` previews without writing; `--yes` skips the prompt.
|
|
24
|
+
- **Single-track by default.** When filters match more than one track, `edit` refuses unless you pass `--multi`. This keeps a too-broad filter from rewriting your whole library.
|
|
25
|
+
- **Rekordbox running:** editing while Rekordbox is open risks conflicts, so `edit` warns (or refuses, in scripting modes).
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Preview a cleanup without touching the database
|
|
31
|
+
rbe edit --title "(Original Mix)" Title --match " (Original Mix)" --replace "" --multi --dry-run
|
|
32
|
+
|
|
33
|
+
# Apply it, confirming each track
|
|
34
|
+
rbe edit --title "(Original Mix)" Title --match " (Original Mix)" --replace "" --multi --interactive
|
|
35
|
+
|
|
36
|
+
# Pipe a search result in and edit those exact tracks
|
|
37
|
+
rbe search --playlist "Mislabeled" --print ids | rbe edit Title --match " " --replace " " --multi --yes
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
See [Filtering](../filtering.md) for the full filter language.
|
|
41
|
+
|
|
42
|
+
## Reference
|
|
43
|
+
|
|
44
|
+
::: mkdocs-click
|
|
45
|
+
:module: rekordbox_edit.cli.edit
|
|
46
|
+
:command: edit_command
|
|
47
|
+
:prog_name: rbe edit
|
|
48
|
+
:depth: 1
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# search
|
|
2
|
+
|
|
3
|
+
Find and display tracks in your Rekordbox database. `search` is read-only: it never modifies the database or your files, which makes it the safe way to rehearse a [filter](../filtering.md) before handing it to `edit` or `convert`.
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Show all FLAC tracks by an artist
|
|
9
|
+
rbe search --artist "Aphex Twin" --format flac
|
|
10
|
+
|
|
11
|
+
# Get all the track IDs in a playlist
|
|
12
|
+
rbe search --playlist "Techno" --print ids
|
|
13
|
+
|
|
14
|
+
# Find tracks matching ALL filters (AND logic)
|
|
15
|
+
rbe search --artist "Burial" --album "Untrue" --match-all
|
|
16
|
+
|
|
17
|
+
# Feed results to another command
|
|
18
|
+
rbe search --artist "Lauryn Hill" --print ids | rbe convert --yes
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
See [Filtering](../filtering.md) for the full filter language and piping recipes.
|
|
22
|
+
|
|
23
|
+
## Reference
|
|
24
|
+
|
|
25
|
+
::: mkdocs-click
|
|
26
|
+
:module: rekordbox_edit.cli.search
|
|
27
|
+
:command: search_command
|
|
28
|
+
:prog_name: rbe search
|
|
29
|
+
:depth: 1
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Filtering
|
|
2
|
+
|
|
3
|
+
Every command (`search`, `edit`, `convert`) selects tracks with the same filter options. The filters decide *which* tracks a command sees; the command's own options decide what happens to them.
|
|
4
|
+
|
|
5
|
+
## Filter Options
|
|
6
|
+
|
|
7
|
+
Repeating a filter, or combining different filters, matches tracks that satisfy *any* of them (OR logic). Pass `--match-all` to require *every* filter to match (AND logic).
|
|
8
|
+
|
|
9
|
+
| Option | Matches tracks whose... |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `--track-id ID` | database track ID equals `ID` |
|
|
12
|
+
| `--title TEXT` | title contains `TEXT` |
|
|
13
|
+
| `--exact-title TEXT` | title is exactly `TEXT` |
|
|
14
|
+
| `--artist TEXT` | artist name contains `TEXT` |
|
|
15
|
+
| `--exact-artist TEXT` | artist name is exactly `TEXT` |
|
|
16
|
+
| `--album TEXT` | album name contains `TEXT` |
|
|
17
|
+
| `--exact-album TEXT` | album name is exactly `TEXT` |
|
|
18
|
+
| `--playlist TEXT` | playlist name contains `TEXT` |
|
|
19
|
+
| `--exact-playlist TEXT` | playlist name is exactly `TEXT` |
|
|
20
|
+
| `--format FMT` | file format is `FMT` (`mp3`, `flac`, `aiff`, `wav`, `m4a`) |
|
|
21
|
+
| `--path TEXT` | file path contains `TEXT` (matched against the folder path, filename, or both) |
|
|
22
|
+
| `--exact-path TEXT` | file path is exactly `TEXT` (resolved to an absolute path before matching) |
|
|
23
|
+
|
|
24
|
+
**Examples:**
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Tracks by either artist (OR)
|
|
28
|
+
rbe search --artist "Daft Punk" --artist "Justice"
|
|
29
|
+
|
|
30
|
+
# Tracks matching artist AND format
|
|
31
|
+
rbe search --artist "Aphex Twin" --format flac --match-all
|
|
32
|
+
|
|
33
|
+
# All the songs in this playlist
|
|
34
|
+
rbe search --exact-playlist "Main Room 2024"
|
|
35
|
+
|
|
36
|
+
# All the songs in all my "house" or "disco" playlists
|
|
37
|
+
rbe search --playlist "house" --playlist "disco"
|
|
38
|
+
|
|
39
|
+
# All the songs in my library that aren't in any playlist
|
|
40
|
+
rbe search --playlist ""
|
|
41
|
+
|
|
42
|
+
# Tracks whose path contains a folder or filename substring
|
|
43
|
+
rbe search --path "Favorites/" --path "track.wav"
|
|
44
|
+
|
|
45
|
+
# The track at an exact location
|
|
46
|
+
rbe search --exact-path "/Users/djmustard/Music/banger.mp3"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Limiting Results
|
|
50
|
+
|
|
51
|
+
- `--first N`: return only the first N results
|
|
52
|
+
- `--last N`: return only the last N results
|
|
53
|
+
|
|
54
|
+
The two are mutually exclusive. They make a great blast radius limiter while you refine a filter: `rbe convert --artist "Crazy Frog" --first 5 --dry-run`.
|
|
55
|
+
|
|
56
|
+
## Track ID Arguments
|
|
57
|
+
|
|
58
|
+
Any positional argument that is not a defined option is interpreted as one or more track IDs:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
rbe search 12345 67890
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Track IDs can also arrive on stdin (see [Scripting and Piping](#scripting-and-piping)). Piped IDs require `--yes` or `--dry-run`, since prompting would interrupt a pipeline.
|
|
65
|
+
|
|
66
|
+
## Output Levels
|
|
67
|
+
|
|
68
|
+
All commands take `--print [silent|ids|info|debug|json]`:
|
|
69
|
+
|
|
70
|
+
- `info` (default): human-readable output
|
|
71
|
+
- `debug`: adds application state detail; debug logs for every run are also written to a log file (the path is shown in `--help`)
|
|
72
|
+
- `silent`: no output
|
|
73
|
+
- `ids`: print only the matching track IDs, space-separated — designed for piping
|
|
74
|
+
- `json`: dump the full response envelope as JSON — a list of [`Track`][rekordbox_edit.models.Track] records plus a result summary (see the response models in the [API Reference](api.md))
|
|
75
|
+
|
|
76
|
+
!!! note
|
|
77
|
+
|
|
78
|
+
`silent`, `ids`, and `json` exist for scripting, so they require `--yes` or `--dry-run`: an interactive confirmation prompt would contradict them.
|
|
79
|
+
|
|
80
|
+
## Scripting and Piping
|
|
81
|
+
|
|
82
|
+
`--print ids` output feeds straight into another command's track-ID arguments:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Convert all of the items found by the initial search command
|
|
86
|
+
rbe search --artist "Lauryn Hill" --print ids | rbe convert --yes
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Piping composes OR and AND logic that a single command cannot express:
|
|
90
|
+
|
|
91
|
+
**AND-narrowing** — pipe a broad OR result into a second command with `--match-all` to intersect:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# (Daft Punk OR Justice) AND flac
|
|
95
|
+
rbe search --artist "Daft Punk" --artist "Justice" --print ids \
|
|
96
|
+
| rbe search --format flac --match-all
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**OR between AND-groups** — merge results from two commands using a subshell:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# (Daft Punk AND flac) OR (Justice AND aiff)
|
|
103
|
+
{ rbe search --artist "Daft Punk" --format flac --match-all --print ids; \
|
|
104
|
+
rbe search --artist "Justice" --format aiff --match-all --print ids; } \
|
|
105
|
+
| rbe convert --format-out mp3 --dry-run
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For richer pipelines, `--print json` emits the same selection as structured data for `jq` or a Python script.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--8<-- "README.md"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
site_name: rekordbox-edit
|
|
2
|
+
site_description: Tools for managing and modifying a RekordBox library en-masse
|
|
3
|
+
site_url: https://rekordbox-edit.readthedocs.io/
|
|
4
|
+
repo_url: https://github.com/jviall/rekordbox-edit
|
|
5
|
+
repo_name: jviall/rekordbox-edit
|
|
6
|
+
|
|
7
|
+
exclude_docs: |
|
|
8
|
+
superpowers/
|
|
9
|
+
|
|
10
|
+
theme:
|
|
11
|
+
name: material
|
|
12
|
+
palette:
|
|
13
|
+
- media: "(prefers-color-scheme: light)"
|
|
14
|
+
scheme: default
|
|
15
|
+
toggle:
|
|
16
|
+
icon: material/brightness-7
|
|
17
|
+
name: Switch to dark mode
|
|
18
|
+
- media: "(prefers-color-scheme: dark)"
|
|
19
|
+
scheme: slate
|
|
20
|
+
toggle:
|
|
21
|
+
icon: material/brightness-4
|
|
22
|
+
name: Switch to light mode
|
|
23
|
+
features:
|
|
24
|
+
- content.code.copy
|
|
25
|
+
|
|
26
|
+
plugins:
|
|
27
|
+
- search
|
|
28
|
+
- mkdocstrings:
|
|
29
|
+
handlers:
|
|
30
|
+
python:
|
|
31
|
+
options:
|
|
32
|
+
filters: ["!^_"]
|
|
33
|
+
show_root_heading: true
|
|
34
|
+
show_source: false
|
|
35
|
+
members_order: source
|
|
36
|
+
|
|
37
|
+
markdown_extensions:
|
|
38
|
+
- admonition
|
|
39
|
+
- attr_list
|
|
40
|
+
- github-callouts
|
|
41
|
+
- mkdocs-click
|
|
42
|
+
- pymdownx.snippets:
|
|
43
|
+
base_path: ["."]
|
|
44
|
+
check_paths: true
|
|
45
|
+
- pymdownx.superfences
|
|
46
|
+
|
|
47
|
+
nav:
|
|
48
|
+
- Getting Started: index.md
|
|
49
|
+
- Filtering: filtering.md
|
|
50
|
+
- Commands:
|
|
51
|
+
- search: commands/search.md
|
|
52
|
+
- edit: commands/edit.md
|
|
53
|
+
- convert: commands/convert.md
|
|
54
|
+
- API Reference: api.md
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rekordbox-edit"
|
|
7
|
-
version = "0.6.0.
|
|
7
|
+
version = "0.6.0.dev40"
|
|
8
8
|
description = "Tools for managing and modifying a RekordBox library en-masse"
|
|
9
9
|
authors = [{ name = "James Viall", email= "jamesviall@pm.me"}]
|
|
10
10
|
license = "MIT"
|
|
@@ -33,6 +33,12 @@ dev = [
|
|
|
33
33
|
"ty>=0.0.1,<1",
|
|
34
34
|
"syrupy>=5,<6",
|
|
35
35
|
]
|
|
36
|
+
docs = [
|
|
37
|
+
"mkdocs-material>=9.5,<10",
|
|
38
|
+
"mkdocstrings[python]>=0.27,<1",
|
|
39
|
+
"mkdocs-click>=0.8,<1",
|
|
40
|
+
"markdown-callouts>=0.4,<1",
|
|
41
|
+
]
|
|
36
42
|
|
|
37
43
|
[tool.pytest.ini_options]
|
|
38
44
|
markers = [
|
|
@@ -95,6 +95,18 @@ global_click_filters = [
|
|
|
95
95
|
multiple=True,
|
|
96
96
|
help="Find tracks of this format",
|
|
97
97
|
),
|
|
98
|
+
click.option(
|
|
99
|
+
"--first",
|
|
100
|
+
type=click.IntRange(min=1),
|
|
101
|
+
default=None,
|
|
102
|
+
help="Return only the first N results",
|
|
103
|
+
),
|
|
104
|
+
click.option(
|
|
105
|
+
"--last",
|
|
106
|
+
type=click.IntRange(min=1),
|
|
107
|
+
default=None,
|
|
108
|
+
help="Return only the last N results",
|
|
109
|
+
),
|
|
98
110
|
click.option(
|
|
99
111
|
"--match-all",
|
|
100
112
|
type=bool,
|
|
@@ -4,9 +4,10 @@ import functools
|
|
|
4
4
|
import logging
|
|
5
5
|
import sys
|
|
6
6
|
from copy import copy
|
|
7
|
+
from typing import TypeVar
|
|
7
8
|
|
|
8
9
|
import click
|
|
9
|
-
from pydantic import BaseModel
|
|
10
|
+
from pydantic import BaseModel, ValidationError
|
|
10
11
|
from pyrekordbox import Rekordbox6Database
|
|
11
12
|
from pyrekordbox.utils import get_rekordbox_pid
|
|
12
13
|
|
|
@@ -17,6 +18,16 @@ logger = logging.getLogger(__name__)
|
|
|
17
18
|
|
|
18
19
|
SCRIPTING_MODES = (PrintChoice.IDS, PrintChoice.SILENT, PrintChoice.JSON)
|
|
19
20
|
|
|
21
|
+
_ArgsT = TypeVar("_ArgsT", bound=BaseModel)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _build_args(model_cls: type[_ArgsT], kwargs: dict) -> _ArgsT:
|
|
25
|
+
"""Construct the command's args model, surfacing validation failures as usage errors."""
|
|
26
|
+
try:
|
|
27
|
+
return model_cls(**kwargs)
|
|
28
|
+
except ValidationError as e:
|
|
29
|
+
raise click.UsageError("; ".join(err["msg"] for err in e.errors())) from e
|
|
30
|
+
|
|
20
31
|
|
|
21
32
|
def _handle_stdin(args) -> bool:
|
|
22
33
|
"""Append track IDs from piped stdin to args.track_ids. Returns True if IDs were piped."""
|
|
@@ -70,6 +81,8 @@ def _narrow_to_track_ids(args, ids: list[str]):
|
|
|
70
81
|
setattr(narrowed, field_name, [])
|
|
71
82
|
narrowed.track_ids = list(ids)
|
|
72
83
|
narrowed.match_all = False
|
|
84
|
+
narrowed.first = None
|
|
85
|
+
narrowed.last = None
|
|
73
86
|
return narrowed
|
|
74
87
|
|
|
75
88
|
|