rekordbox-edit 0.4.0.dev38__tar.gz → 0.4.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.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/CHANGELOG.md +4 -1
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/PKG-INFO +4 -4
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/README.md +3 -3
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/pyproject.toml +1 -1
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/_click.py +19 -0
- rekordbox_edit-0.4.0.dev40/rekordbox_edit/args.py +54 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/commands/convert.py +18 -31
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/commands/edit.py +16 -15
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/commands/search.py +20 -49
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/query.py +31 -54
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/commands/test_convert.py +10 -10
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/commands/test_edit.py +4 -4
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/commands/test_search.py +12 -12
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/test_query.py +26 -23
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/uv.lock +1 -1
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.agent-style/RULES.md +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.agent-style/claude-code.md +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/actions/commitizen-bump/action.yml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/actions/commitizen-bump/commitizen-bump.sh +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/actions/install/action.yml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/actions/lint/action.yml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/actions/test/action.yml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/workflows/cd.yml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/workflows/ci.yml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/workflows/publish.yml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/workflows/release.yml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.gitignore +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.pre-commit-config.yaml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/AGENTS.md +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/CLAUDE.md +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/CONTRIBUTING.md +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/LICENSE +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/Makefile +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/codecov.yml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/__init__.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/cli.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/commands/__init__.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/display.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/logger.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/utils.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/renovate.json5 +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/ruff.toml +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/__init__.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/commands/__init__.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/conftest.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/test_display.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/test_logger.py +0 -0
- {rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
## v0.4.0.
|
|
1
|
+
## v0.4.0.dev40 (2026-06-05)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
- docs: update README.md
|
|
5
|
+
- refactor(query): group filter args into FilterArgs dataclass
|
|
6
|
+
- refactor(cli): extract convert-specific options into convert_click_options
|
|
4
7
|
- refactor(cli): extract edit-specific options into edit_click_options
|
|
5
8
|
- refactor(cli): extract shared confirmation flags into global_click_confirmations
|
|
6
9
|
- chore(deps): update linters to v0.0.42 (#58)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rekordbox-edit
|
|
3
|
-
Version: 0.4.0.
|
|
3
|
+
Version: 0.4.0.dev40
|
|
4
4
|
Summary: Tools for managing and modifying a RekordBox library en-masse
|
|
5
5
|
Project-URL: Homepage, https://github.com/jviall/rekordbox-edit
|
|
6
6
|
Project-URL: Repository, https://github.com/jviall/rekordbox-edit
|
|
@@ -244,9 +244,9 @@ And generally limit the potential impact of a mistake by using filters to target
|
|
|
244
244
|
|
|
245
245
|
## AI Usage
|
|
246
246
|
|
|
247
|
-
I believe it's important to be aware of and to disclose AI usage.
|
|
247
|
+
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.
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
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.
|
|
250
250
|
|
|
251
251
|
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!
|
|
252
252
|
|
|
@@ -254,7 +254,7 @@ If it's any consolation, my main test subject has been my own 10,000+ track Reko
|
|
|
254
254
|
|
|
255
255
|
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.
|
|
256
256
|
|
|
257
|
-
I built this tool to help correct my own bad habits and
|
|
257
|
+
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.
|
|
258
258
|
|
|
259
259
|
## Contributing
|
|
260
260
|
|
|
@@ -225,9 +225,9 @@ And generally limit the potential impact of a mistake by using filters to target
|
|
|
225
225
|
|
|
226
226
|
## AI Usage
|
|
227
227
|
|
|
228
|
-
I believe it's important to be aware of and to disclose AI usage.
|
|
228
|
+
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.
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
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.
|
|
231
231
|
|
|
232
232
|
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!
|
|
233
233
|
|
|
@@ -235,7 +235,7 @@ If it's any consolation, my main test subject has been my own 10,000+ track Reko
|
|
|
235
235
|
|
|
236
236
|
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.
|
|
237
237
|
|
|
238
|
-
I built this tool to help correct my own bad habits and
|
|
238
|
+
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.
|
|
239
239
|
|
|
240
240
|
## Contributing
|
|
241
241
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rekordbox-edit"
|
|
7
|
-
version = "0.4.0.
|
|
7
|
+
version = "0.4.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"
|
|
@@ -142,6 +142,25 @@ edit_click_options = [
|
|
|
142
142
|
),
|
|
143
143
|
]
|
|
144
144
|
|
|
145
|
+
convert_click_options = [
|
|
146
|
+
click.option(
|
|
147
|
+
"--delete/--keep",
|
|
148
|
+
default=None,
|
|
149
|
+
help="Delete or keep original files after conversion (default: delete for lossless, keep for MP3)",
|
|
150
|
+
),
|
|
151
|
+
click.option(
|
|
152
|
+
"--overwrite",
|
|
153
|
+
is_flag=True,
|
|
154
|
+
help="Overwrite existing output files instead of skipping them",
|
|
155
|
+
),
|
|
156
|
+
click.option(
|
|
157
|
+
"--format-out",
|
|
158
|
+
type=click.Choice(["aiff", "flac", "wav", "alac", "mp3"], case_sensitive=False),
|
|
159
|
+
default="aiff",
|
|
160
|
+
help="Output format (default: aiff)",
|
|
161
|
+
),
|
|
162
|
+
]
|
|
163
|
+
|
|
145
164
|
|
|
146
165
|
def add_click_options(options):
|
|
147
166
|
def _add_options(func):
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Dataclass containers for CLI argument groups.
|
|
2
|
+
|
|
3
|
+
These types are the public API of the functional layer below the CLI: callers
|
|
4
|
+
of `get_filtered_content` and the private command helpers receive them in lieu
|
|
5
|
+
of long flat parameter lists. Each `*_from_kwargs` factory packs the matching
|
|
6
|
+
Click parameters into its dataclass.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class FilterArgs:
|
|
14
|
+
"""Filter inputs forwarded to `get_filtered_content`.
|
|
15
|
+
|
|
16
|
+
Field names mirror the Click parameter names: `track_ids` holds the
|
|
17
|
+
positional TRACK_IDS argument (variadic), `track_id` holds the values of
|
|
18
|
+
the repeated `--track-id` option.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
track_id: list[str] = field(default_factory=list)
|
|
22
|
+
track_ids: list[str] = field(default_factory=list)
|
|
23
|
+
title: list[str] = field(default_factory=list)
|
|
24
|
+
exact_title: list[str] = field(default_factory=list)
|
|
25
|
+
playlist: list[str] = field(default_factory=list)
|
|
26
|
+
exact_playlist: list[str] = field(default_factory=list)
|
|
27
|
+
artist: list[str] = field(default_factory=list)
|
|
28
|
+
exact_artist: list[str] = field(default_factory=list)
|
|
29
|
+
album: list[str] = field(default_factory=list)
|
|
30
|
+
exact_album: list[str] = field(default_factory=list)
|
|
31
|
+
path: list[str] = field(default_factory=list)
|
|
32
|
+
exact_path: list[str] = field(default_factory=list)
|
|
33
|
+
format: list[str] = field(default_factory=list)
|
|
34
|
+
match_all: bool = False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def filter_args_from_kwargs(**kwargs) -> FilterArgs:
|
|
38
|
+
"""Pack the flat Click kwargs for the `global_click_filters` group into a FilterArgs."""
|
|
39
|
+
return FilterArgs(
|
|
40
|
+
track_id=list(kwargs.get("track_id") or []),
|
|
41
|
+
track_ids=list(kwargs.get("track_ids") or []),
|
|
42
|
+
title=list(kwargs.get("title") or []),
|
|
43
|
+
exact_title=list(kwargs.get("exact_title") or []),
|
|
44
|
+
playlist=list(kwargs.get("playlist") or []),
|
|
45
|
+
exact_playlist=list(kwargs.get("exact_playlist") or []),
|
|
46
|
+
artist=list(kwargs.get("artist") or []),
|
|
47
|
+
exact_artist=list(kwargs.get("exact_artist") or []),
|
|
48
|
+
album=list(kwargs.get("album") or []),
|
|
49
|
+
exact_album=list(kwargs.get("exact_album") or []),
|
|
50
|
+
path=list(kwargs.get("path") or []),
|
|
51
|
+
exact_path=list(kwargs.get("exact_path") or []),
|
|
52
|
+
format=list(kwargs.get("format") or []),
|
|
53
|
+
match_all=bool(kwargs.get("match_all", False)),
|
|
54
|
+
)
|
{rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/commands/convert.py
RENAMED
|
@@ -16,11 +16,13 @@ from pyrekordbox.utils import get_rekordbox_pid
|
|
|
16
16
|
from rekordbox_edit._click import (
|
|
17
17
|
PrintChoice,
|
|
18
18
|
add_click_options,
|
|
19
|
+
convert_click_options,
|
|
19
20
|
global_click_confirmations,
|
|
20
21
|
global_click_filters,
|
|
21
22
|
print_option,
|
|
22
23
|
track_ids_argument,
|
|
23
24
|
)
|
|
25
|
+
from rekordbox_edit.args import filter_args_from_kwargs
|
|
24
26
|
from rekordbox_edit.logger import get_debug_file_path, set_level
|
|
25
27
|
from rekordbox_edit.query import get_filtered_content
|
|
26
28
|
from rekordbox_edit.display import PrintableField, print_track_info
|
|
@@ -245,26 +247,11 @@ def get_output_path(content, output_format) -> Tuple[str, str, str]:
|
|
|
245
247
|
@click.command(
|
|
246
248
|
epilog=f"Debug logs for each run can be found at:\n{get_debug_file_path().parent}"
|
|
247
249
|
)
|
|
248
|
-
@click.option(
|
|
249
|
-
"--delete/--keep",
|
|
250
|
-
default=None,
|
|
251
|
-
help="Delete or keep original files after conversion (default: delete for lossless, keep for MP3)",
|
|
252
|
-
)
|
|
253
|
-
@click.option(
|
|
254
|
-
"--overwrite",
|
|
255
|
-
is_flag=True,
|
|
256
|
-
help="Overwrite existing output files instead of skipping them",
|
|
257
|
-
)
|
|
258
|
-
@click.option(
|
|
259
|
-
"--format-out",
|
|
260
|
-
type=click.Choice(["aiff", "flac", "wav", "alac", "mp3"], case_sensitive=False),
|
|
261
|
-
default="aiff",
|
|
262
|
-
help="Output format (default: aiff)",
|
|
263
|
-
)
|
|
264
250
|
@add_click_options(
|
|
265
251
|
[
|
|
266
252
|
*global_click_filters,
|
|
267
253
|
*global_click_confirmations,
|
|
254
|
+
*convert_click_options,
|
|
268
255
|
print_option,
|
|
269
256
|
track_ids_argument,
|
|
270
257
|
]
|
|
@@ -375,23 +362,23 @@ def convert_command(
|
|
|
375
362
|
logger.debug("Database connection established")
|
|
376
363
|
|
|
377
364
|
# === QUERY & FILTER ===
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
exact_paths=exact_path,
|
|
365
|
+
filters = filter_args_from_kwargs(
|
|
366
|
+
track_id=track_id,
|
|
367
|
+
track_ids=track_ids,
|
|
368
|
+
playlist=playlist,
|
|
369
|
+
exact_playlist=exact_playlist,
|
|
370
|
+
album=album,
|
|
371
|
+
exact_album=exact_album,
|
|
372
|
+
artist=artist,
|
|
373
|
+
exact_artist=exact_artist,
|
|
374
|
+
title=title,
|
|
375
|
+
exact_title=exact_title,
|
|
376
|
+
path=path,
|
|
377
|
+
exact_path=exact_path,
|
|
378
|
+
format=format,
|
|
393
379
|
match_all=match_all,
|
|
394
380
|
)
|
|
381
|
+
result = get_filtered_content(db, filters)
|
|
395
382
|
filtered_content = result.scalars().all()
|
|
396
383
|
logger.debug(f"Query returned {len(filtered_content)} tracks")
|
|
397
384
|
|
|
@@ -16,6 +16,7 @@ from rekordbox_edit._click import (
|
|
|
16
16
|
print_option,
|
|
17
17
|
track_ids_argument,
|
|
18
18
|
)
|
|
19
|
+
from rekordbox_edit.args import filter_args_from_kwargs
|
|
19
20
|
from rekordbox_edit.logger import get_debug_file_path, set_level
|
|
20
21
|
from rekordbox_edit.query import get_filtered_content
|
|
21
22
|
from rekordbox_edit.display import PrintableField, print_track_info
|
|
@@ -106,23 +107,23 @@ def edit_command(
|
|
|
106
107
|
if not db.session:
|
|
107
108
|
raise RuntimeError("Failed to connect to Rekordbox Database: No Session.")
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
formats=format,
|
|
110
|
+
filters = filter_args_from_kwargs(
|
|
111
|
+
track_id=track_id,
|
|
112
|
+
track_ids=track_ids,
|
|
113
|
+
playlist=playlist,
|
|
114
|
+
exact_playlist=exact_playlist,
|
|
115
|
+
album=album,
|
|
116
|
+
exact_album=exact_album,
|
|
117
|
+
artist=artist,
|
|
118
|
+
exact_artist=exact_artist,
|
|
119
|
+
title=title,
|
|
120
|
+
exact_title=exact_title,
|
|
121
|
+
path=path,
|
|
122
|
+
exact_path=exact_path,
|
|
123
|
+
format=format,
|
|
124
124
|
match_all=match_all,
|
|
125
125
|
)
|
|
126
|
+
result = get_filtered_content(db, filters)
|
|
126
127
|
tracks = result.scalars().all()
|
|
127
128
|
|
|
128
129
|
col_name = FIELD_COLUMNS[field]
|
|
@@ -14,6 +14,7 @@ from rekordbox_edit._click import (
|
|
|
14
14
|
print_option,
|
|
15
15
|
track_ids_argument,
|
|
16
16
|
)
|
|
17
|
+
from rekordbox_edit.args import filter_args_from_kwargs
|
|
17
18
|
from rekordbox_edit.logger import get_debug_file_path, set_level
|
|
18
19
|
from rekordbox_edit.query import get_filtered_content
|
|
19
20
|
from rekordbox_edit.display import print_track_info
|
|
@@ -51,61 +52,31 @@ def search_command(
|
|
|
51
52
|
if stdin_data:
|
|
52
53
|
track_ids = list(track_ids or []) + stdin_data.split()
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
filters = filter_args_from_kwargs(
|
|
56
|
+
track_id=track_id,
|
|
57
|
+
track_ids=track_ids,
|
|
58
|
+
playlist=playlist,
|
|
59
|
+
exact_playlist=exact_playlist,
|
|
60
|
+
album=album,
|
|
61
|
+
exact_album=exact_album,
|
|
62
|
+
artist=artist,
|
|
63
|
+
exact_artist=exact_artist,
|
|
64
|
+
title=title,
|
|
65
|
+
exact_title=exact_title,
|
|
66
|
+
path=path,
|
|
67
|
+
exact_path=exact_path,
|
|
68
|
+
format=format,
|
|
69
|
+
match_all=match_all,
|
|
70
|
+
)
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if track_ids:
|
|
59
|
-
filters.append(f"track_ids={track_ids}")
|
|
60
|
-
if track_id:
|
|
61
|
-
filters.append(f"track_id={track_id}")
|
|
62
|
-
if artist:
|
|
63
|
-
filters.append(f"artist={artist}")
|
|
64
|
-
if exact_artist:
|
|
65
|
-
filters.append(f"exact_artist={exact_artist}")
|
|
66
|
-
if title:
|
|
67
|
-
filters.append(f"title={title}")
|
|
68
|
-
if exact_title:
|
|
69
|
-
filters.append(f"exact_title={exact_title}")
|
|
70
|
-
if album:
|
|
71
|
-
filters.append(f"album={album}")
|
|
72
|
-
if exact_album:
|
|
73
|
-
filters.append(f"exact_album={exact_album}")
|
|
74
|
-
if playlist:
|
|
75
|
-
filters.append(f"playlist={playlist}")
|
|
76
|
-
if exact_playlist:
|
|
77
|
-
filters.append(f"exact_playlist={exact_playlist}")
|
|
78
|
-
if path:
|
|
79
|
-
filters.append(f"path={path}")
|
|
80
|
-
if exact_path:
|
|
81
|
-
filters.append(f"exact_path={exact_path}")
|
|
82
|
-
if format:
|
|
83
|
-
filters.append(f"format={format}")
|
|
84
|
-
if match_all:
|
|
85
|
-
filters.append("match_all=True")
|
|
86
|
-
logger.debug(f"Search filters: {', '.join(filters) if filters else 'none'}")
|
|
72
|
+
logger.debug(f"Search filters: {filters}")
|
|
73
|
+
logger.debug("Connecting to RekordBox database...")
|
|
87
74
|
|
|
88
75
|
db = Rekordbox6Database()
|
|
89
76
|
if not db.session:
|
|
90
77
|
raise RuntimeError("Failed to connect to Rekordbox Database: No Session.")
|
|
91
78
|
|
|
92
|
-
filtered_result = get_filtered_content(
|
|
93
|
-
db,
|
|
94
|
-
track_id_args=track_ids,
|
|
95
|
-
track_ids=track_id,
|
|
96
|
-
playlists=playlist,
|
|
97
|
-
exact_playlists=exact_playlist,
|
|
98
|
-
artists=artist,
|
|
99
|
-
exact_artists=exact_artist,
|
|
100
|
-
albums=album,
|
|
101
|
-
exact_albums=exact_album,
|
|
102
|
-
titles=title,
|
|
103
|
-
exact_titles=exact_title,
|
|
104
|
-
paths=path,
|
|
105
|
-
exact_paths=exact_path,
|
|
106
|
-
formats=format,
|
|
107
|
-
match_all=match_all,
|
|
108
|
-
)
|
|
79
|
+
filtered_result = get_filtered_content(db, filters)
|
|
109
80
|
|
|
110
81
|
if print_opt is PrintChoice.SILENT:
|
|
111
82
|
pass
|
|
@@ -13,6 +13,8 @@ from pyrekordbox.db6.tables import (
|
|
|
13
13
|
from sqlalchemy import ColumnElement, Result, and_, func, or_, select
|
|
14
14
|
from sqlalchemy.orm import aliased
|
|
15
15
|
|
|
16
|
+
from rekordbox_edit.args import FilterArgs
|
|
17
|
+
|
|
16
18
|
logger = logging.getLogger(__name__)
|
|
17
19
|
|
|
18
20
|
|
|
@@ -247,20 +249,7 @@ class CollectionQuery:
|
|
|
247
249
|
|
|
248
250
|
def get_filtered_content(
|
|
249
251
|
db: Rekordbox6Database,
|
|
250
|
-
|
|
251
|
-
track_ids: List[str] | None = None,
|
|
252
|
-
formats: List[str] | None = None,
|
|
253
|
-
playlists: List[str] | None = None,
|
|
254
|
-
exact_playlists: List[str] | None = None,
|
|
255
|
-
artists: List[str] | None = None,
|
|
256
|
-
exact_artists: List[str] | None = None,
|
|
257
|
-
albums: List[str] | None = None,
|
|
258
|
-
exact_albums: List[str] | None = None,
|
|
259
|
-
titles: List[str] | None = None,
|
|
260
|
-
exact_titles: List[str] | None = None,
|
|
261
|
-
paths: List[str] | None = None,
|
|
262
|
-
exact_paths: List[str] | None = None,
|
|
263
|
-
match_all: bool = False,
|
|
252
|
+
filters: FilterArgs,
|
|
264
253
|
) -> Result[Tuple[DjmdContent]]:
|
|
265
254
|
"""Query the Rekordbox database with the provided filters."""
|
|
266
255
|
db = db if db is not None else Rekordbox6Database()
|
|
@@ -269,59 +258,47 @@ def get_filtered_content(
|
|
|
269
258
|
|
|
270
259
|
query = CollectionQuery()
|
|
271
260
|
|
|
272
|
-
if
|
|
273
|
-
logger.debug(f"Filtering by {len(
|
|
274
|
-
query = query.by_track_ids(track_ids=
|
|
261
|
+
if filters.track_ids:
|
|
262
|
+
logger.debug(f"Filtering by {len(filters.track_ids)} track ID argument(s)")
|
|
263
|
+
query = query.by_track_ids(track_ids=filters.track_ids)
|
|
275
264
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
query = query.by_track_ids(track_id)
|
|
265
|
+
for tid in filters.track_id:
|
|
266
|
+
query = query.by_track_ids(tid)
|
|
279
267
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
query = query.by_format(fmt)
|
|
268
|
+
for fmt in filters.format:
|
|
269
|
+
query = query.by_format(fmt)
|
|
283
270
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
query = query.by_playlist(playlist)
|
|
271
|
+
for playlist in filters.playlist:
|
|
272
|
+
query = query.by_playlist(playlist)
|
|
287
273
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
query = query.by_playlist(exact_playlist, exact=True)
|
|
274
|
+
for exact_playlist in filters.exact_playlist:
|
|
275
|
+
query = query.by_playlist(exact_playlist, exact=True)
|
|
291
276
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
query = query.by_artist(artist)
|
|
277
|
+
for artist in filters.artist:
|
|
278
|
+
query = query.by_artist(artist)
|
|
295
279
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
query = query.by_artist(exact_artist, exact=True)
|
|
280
|
+
for exact_artist in filters.exact_artist:
|
|
281
|
+
query = query.by_artist(exact_artist, exact=True)
|
|
299
282
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
query = query.by_album(album)
|
|
283
|
+
for album in filters.album:
|
|
284
|
+
query = query.by_album(album)
|
|
303
285
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
query = query.by_album(exact_album, exact=True)
|
|
286
|
+
for exact_album in filters.exact_album:
|
|
287
|
+
query = query.by_album(exact_album, exact=True)
|
|
307
288
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
query = query.by_title(title)
|
|
289
|
+
for title in filters.title:
|
|
290
|
+
query = query.by_title(title)
|
|
311
291
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
query = query.by_title(title, exact=True)
|
|
292
|
+
for title in filters.exact_title:
|
|
293
|
+
query = query.by_title(title, exact=True)
|
|
315
294
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
query = query.by_path(path)
|
|
295
|
+
for path in filters.path:
|
|
296
|
+
query = query.by_path(path)
|
|
319
297
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
query = query.by_path(exact_path, exact=True)
|
|
298
|
+
for exact_path in filters.exact_path:
|
|
299
|
+
query = query.by_path(exact_path, exact=True)
|
|
323
300
|
|
|
324
|
-
if match_all:
|
|
301
|
+
if filters.match_all:
|
|
325
302
|
query = query.match_all()
|
|
326
303
|
|
|
327
304
|
return query.execute(db)
|
|
@@ -709,10 +709,10 @@ class TestConvertCommand:
|
|
|
709
709
|
["--dry-run", "--artist", "Daft Punk", "--format", "flac", "--match-all"],
|
|
710
710
|
)
|
|
711
711
|
|
|
712
|
-
|
|
713
|
-
assert
|
|
714
|
-
assert
|
|
715
|
-
assert
|
|
712
|
+
filters = mock_get_filtered_content.call_args.args[1]
|
|
713
|
+
assert filters.artist == ["Daft Punk"]
|
|
714
|
+
assert filters.format == ["flac"]
|
|
715
|
+
assert filters.match_all is True
|
|
716
716
|
|
|
717
717
|
@patch("rekordbox_edit.commands.convert.get_rekordbox_pid")
|
|
718
718
|
@patch("rekordbox_edit.commands.convert.confirm")
|
|
@@ -2074,8 +2074,8 @@ class TestConvertStdinPiping:
|
|
|
2074
2074
|
input="190993005 108916663 59476253",
|
|
2075
2075
|
)
|
|
2076
2076
|
|
|
2077
|
-
|
|
2078
|
-
assert
|
|
2077
|
+
filters = mock_get_filtered_content.call_args.args[1]
|
|
2078
|
+
assert filters.track_ids == ["190993005", "108916663", "59476253"]
|
|
2079
2079
|
|
|
2080
2080
|
@patch("rekordbox_edit.commands.convert.get_rekordbox_pid")
|
|
2081
2081
|
@patch("rekordbox_edit.commands.convert.get_filtered_content")
|
|
@@ -2109,8 +2109,8 @@ class TestConvertStdinPiping:
|
|
|
2109
2109
|
input="59476253 113475696",
|
|
2110
2110
|
)
|
|
2111
2111
|
|
|
2112
|
-
|
|
2113
|
-
assert
|
|
2112
|
+
filters = mock_get_filtered_content.call_args.args[1]
|
|
2113
|
+
assert filters.track_ids == [
|
|
2114
2114
|
"190993005",
|
|
2115
2115
|
"108916663",
|
|
2116
2116
|
"59476253",
|
|
@@ -2154,5 +2154,5 @@ class TestConvertStdinPiping:
|
|
|
2154
2154
|
runner = CliRunner()
|
|
2155
2155
|
runner.invoke(convert_command, ["--dry-run"], input=" ")
|
|
2156
2156
|
|
|
2157
|
-
|
|
2158
|
-
assert not
|
|
2157
|
+
filters = mock_get_filtered_content.call_args.args[1]
|
|
2158
|
+
assert not filters.track_ids
|
|
@@ -219,10 +219,10 @@ class TestEditCommandPhase1:
|
|
|
219
219
|
],
|
|
220
220
|
)
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
assert
|
|
224
|
-
assert
|
|
225
|
-
assert
|
|
222
|
+
filters = mock_gfc.call_args.args[1]
|
|
223
|
+
assert filters.artist == ["Bicep"]
|
|
224
|
+
assert filters.format == ["flac"]
|
|
225
|
+
assert filters.match_all is True
|
|
226
226
|
|
|
227
227
|
@patch("rekordbox_edit.commands.edit.confirm")
|
|
228
228
|
@patch("rekordbox_edit.commands.edit.get_filtered_content")
|
|
@@ -150,12 +150,12 @@ class TestSearchCommand:
|
|
|
150
150
|
],
|
|
151
151
|
)
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
assert
|
|
155
|
-
assert
|
|
156
|
-
assert
|
|
157
|
-
assert
|
|
158
|
-
assert
|
|
153
|
+
filters = mock_get_filtered_content.call_args.args[1]
|
|
154
|
+
assert filters.artist == ["Daft Punk"]
|
|
155
|
+
assert filters.format == ["flac"]
|
|
156
|
+
assert filters.path == ["song.mp3"]
|
|
157
|
+
assert filters.exact_path == ["/Music/album/track.wav"]
|
|
158
|
+
assert filters.match_all is True
|
|
159
159
|
|
|
160
160
|
|
|
161
161
|
class TestSearchStdinPiping:
|
|
@@ -184,8 +184,8 @@ class TestSearchStdinPiping:
|
|
|
184
184
|
runner = CliRunner()
|
|
185
185
|
runner.invoke(search_command, [], input="190993005 108916663 59476253")
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
assert
|
|
187
|
+
filters = mock_get_filtered_content.call_args.args[1]
|
|
188
|
+
assert filters.track_ids == ["190993005", "108916663", "59476253"]
|
|
189
189
|
|
|
190
190
|
@patch("rekordbox_edit.commands.search.print_track_info")
|
|
191
191
|
@patch("rekordbox_edit.commands.search.get_filtered_content")
|
|
@@ -214,8 +214,8 @@ class TestSearchStdinPiping:
|
|
|
214
214
|
input="59476253 113475696",
|
|
215
215
|
)
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
assert
|
|
217
|
+
filters = mock_get_filtered_content.call_args.args[1]
|
|
218
|
+
assert filters.track_ids == [
|
|
219
219
|
"190993005",
|
|
220
220
|
"108916663",
|
|
221
221
|
"59476253",
|
|
@@ -245,5 +245,5 @@ class TestSearchStdinPiping:
|
|
|
245
245
|
runner = CliRunner()
|
|
246
246
|
runner.invoke(search_command, [], input=" ")
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
assert not
|
|
248
|
+
filters = mock_get_filtered_content.call_args.args[1]
|
|
249
|
+
assert not filters.track_ids
|
|
@@ -9,6 +9,7 @@ from unittest.mock import MagicMock
|
|
|
9
9
|
import pytest
|
|
10
10
|
from sqlalchemy import ColumnElement
|
|
11
11
|
|
|
12
|
+
from rekordbox_edit.args import FilterArgs
|
|
12
13
|
from rekordbox_edit.query import CollectionQuery, get_filtered_content
|
|
13
14
|
|
|
14
15
|
|
|
@@ -590,7 +591,7 @@ class TestGetFilteredContent:
|
|
|
590
591
|
"""Tests for the get_filtered_content function."""
|
|
591
592
|
|
|
592
593
|
def test_no_filters(self, mock_db, mock_query):
|
|
593
|
-
get_filtered_content(mock_db)
|
|
594
|
+
get_filtered_content(mock_db, FilterArgs())
|
|
594
595
|
mock_query.by_track_ids.assert_not_called()
|
|
595
596
|
mock_query.by_artist.assert_not_called()
|
|
596
597
|
mock_query.by_title.assert_not_called()
|
|
@@ -601,83 +602,84 @@ class TestGetFilteredContent:
|
|
|
601
602
|
mock_query.execute.assert_called_once_with(mock_db)
|
|
602
603
|
|
|
603
604
|
def test_track_id_args(self, mock_db, mock_query):
|
|
604
|
-
get_filtered_content(mock_db,
|
|
605
|
+
get_filtered_content(mock_db, FilterArgs(track_ids=["123", "456"]))
|
|
605
606
|
mock_query.by_track_ids.assert_called_once_with(track_ids=["123", "456"])
|
|
606
607
|
|
|
607
608
|
def test_track_ids(self, mock_db, mock_query):
|
|
608
|
-
get_filtered_content(mock_db,
|
|
609
|
+
get_filtered_content(mock_db, FilterArgs(track_id=["123", "456"]))
|
|
609
610
|
assert mock_query.by_track_ids.call_count == 2
|
|
610
611
|
mock_query.by_track_ids.assert_any_call("123")
|
|
611
612
|
mock_query.by_track_ids.assert_any_call("456")
|
|
612
613
|
|
|
613
614
|
def test_artist(self, mock_db, mock_query):
|
|
614
|
-
get_filtered_content(mock_db,
|
|
615
|
+
get_filtered_content(mock_db, FilterArgs(artist=["Daft Punk"]))
|
|
615
616
|
mock_query.by_artist.assert_called_once_with("Daft Punk")
|
|
616
617
|
|
|
617
618
|
def test_multiple_artists(self, mock_db, mock_query):
|
|
618
|
-
get_filtered_content(mock_db,
|
|
619
|
+
get_filtered_content(mock_db, FilterArgs(artist=["Daft Punk", "Justice"]))
|
|
619
620
|
assert mock_query.by_artist.call_count == 2
|
|
620
621
|
mock_query.by_artist.assert_any_call("Daft Punk")
|
|
621
622
|
mock_query.by_artist.assert_any_call("Justice")
|
|
622
623
|
|
|
623
624
|
def test_exact_artist(self, mock_db, mock_query):
|
|
624
|
-
get_filtered_content(mock_db,
|
|
625
|
+
get_filtered_content(mock_db, FilterArgs(exact_artist=["Daft Punk"]))
|
|
625
626
|
mock_query.by_artist.assert_called_once_with("Daft Punk", exact=True)
|
|
626
627
|
|
|
627
628
|
def test_title(self, mock_db, mock_query):
|
|
628
|
-
get_filtered_content(mock_db,
|
|
629
|
+
get_filtered_content(mock_db, FilterArgs(title=["One More Time"]))
|
|
629
630
|
mock_query.by_title.assert_called_once_with("One More Time")
|
|
630
631
|
|
|
631
632
|
def test_exact_title(self, mock_db, mock_query):
|
|
632
|
-
get_filtered_content(mock_db,
|
|
633
|
+
get_filtered_content(mock_db, FilterArgs(exact_title=["One More Time"]))
|
|
633
634
|
mock_query.by_title.assert_called_once_with("One More Time", exact=True)
|
|
634
635
|
|
|
635
636
|
def test_album(self, mock_db, mock_query):
|
|
636
|
-
get_filtered_content(mock_db,
|
|
637
|
+
get_filtered_content(mock_db, FilterArgs(album=["Discovery"]))
|
|
637
638
|
mock_query.by_album.assert_called_once_with("Discovery")
|
|
638
639
|
|
|
639
640
|
def test_exact_album(self, mock_db, mock_query):
|
|
640
|
-
get_filtered_content(mock_db,
|
|
641
|
+
get_filtered_content(mock_db, FilterArgs(exact_album=["Discovery"]))
|
|
641
642
|
mock_query.by_album.assert_called_once_with("Discovery", exact=True)
|
|
642
643
|
|
|
643
644
|
def test_playlist(self, mock_db, mock_query):
|
|
644
|
-
get_filtered_content(mock_db,
|
|
645
|
+
get_filtered_content(mock_db, FilterArgs(playlist=["My Playlist"]))
|
|
645
646
|
mock_query.by_playlist.assert_called_once_with("My Playlist")
|
|
646
647
|
|
|
647
648
|
def test_exact_playlist(self, mock_db, mock_query):
|
|
648
|
-
get_filtered_content(mock_db,
|
|
649
|
+
get_filtered_content(mock_db, FilterArgs(exact_playlist=["My Playlist"]))
|
|
649
650
|
mock_query.by_playlist.assert_called_once_with("My Playlist", exact=True)
|
|
650
651
|
|
|
651
652
|
def test_path(self, mock_db, mock_query):
|
|
652
|
-
get_filtered_content(mock_db,
|
|
653
|
+
get_filtered_content(mock_db, FilterArgs(path=["Music/track.mp3"]))
|
|
653
654
|
mock_query.by_path.assert_called_once_with("Music/track.mp3")
|
|
654
655
|
|
|
655
656
|
def test_exact_path(self, mock_db, mock_query):
|
|
656
|
-
get_filtered_content(mock_db,
|
|
657
|
+
get_filtered_content(mock_db, FilterArgs(exact_path=["/Music/track.wav"]))
|
|
657
658
|
mock_query.by_path.assert_called_once_with("/Music/track.wav", exact=True)
|
|
658
659
|
|
|
659
660
|
def test_format(self, mock_db, mock_query):
|
|
660
|
-
get_filtered_content(mock_db,
|
|
661
|
+
get_filtered_content(mock_db, FilterArgs(format=["flac"]))
|
|
661
662
|
mock_query.by_format.assert_called_once_with("flac")
|
|
662
663
|
|
|
663
664
|
def test_multiple_formats(self, mock_db, mock_query):
|
|
664
|
-
get_filtered_content(mock_db,
|
|
665
|
+
get_filtered_content(mock_db, FilterArgs(format=["flac", "aiff"]))
|
|
665
666
|
assert mock_query.by_format.call_count == 2
|
|
666
667
|
mock_query.by_format.assert_any_call("flac")
|
|
667
668
|
mock_query.by_format.assert_any_call("aiff")
|
|
668
669
|
|
|
669
670
|
def test_match_all(self, mock_db, mock_query):
|
|
670
|
-
get_filtered_content(mock_db,
|
|
671
|
+
get_filtered_content(mock_db, FilterArgs(artist=["Daft Punk"], match_all=True))
|
|
671
672
|
mock_query.match_all.assert_called_once()
|
|
672
673
|
|
|
673
674
|
def test_default_no_match_all(self, mock_db, mock_query):
|
|
674
|
-
get_filtered_content(mock_db,
|
|
675
|
+
get_filtered_content(mock_db, FilterArgs(artist=["Daft Punk"]))
|
|
675
676
|
mock_query.match_all.assert_not_called()
|
|
676
677
|
|
|
677
678
|
def test_track_id_args_combined_with_format(self, mock_db, mock_query):
|
|
678
|
-
"""
|
|
679
|
+
"""Positional track IDs should combine with other filters, not override them."""
|
|
679
680
|
get_filtered_content(
|
|
680
|
-
mock_db,
|
|
681
|
+
mock_db,
|
|
682
|
+
FilterArgs(track_ids=["123"], format=["flac"], match_all=True),
|
|
681
683
|
)
|
|
682
684
|
mock_query.by_track_ids.assert_called_once_with(track_ids=["123"])
|
|
683
685
|
mock_query.by_format.assert_called_once_with("flac")
|
|
@@ -686,7 +688,8 @@ class TestGetFilteredContent:
|
|
|
686
688
|
def test_track_id_args_combined_with_artist(self, mock_db, mock_query):
|
|
687
689
|
"""Piped IDs + artist filter with match_all narrows to artist within that ID set."""
|
|
688
690
|
get_filtered_content(
|
|
689
|
-
mock_db,
|
|
691
|
+
mock_db,
|
|
692
|
+
FilterArgs(track_ids=["123", "456"], artist=["Justice"], match_all=True),
|
|
690
693
|
)
|
|
691
694
|
mock_query.by_track_ids.assert_called_once_with(track_ids=["123", "456"])
|
|
692
695
|
mock_query.by_artist.assert_called_once_with("Justice")
|
|
@@ -694,7 +697,7 @@ class TestGetFilteredContent:
|
|
|
694
697
|
|
|
695
698
|
def test_track_id_args_or_with_artist(self, mock_db, mock_query):
|
|
696
699
|
"""Piped IDs OR'd with artist expands the result set."""
|
|
697
|
-
get_filtered_content(mock_db,
|
|
700
|
+
get_filtered_content(mock_db, FilterArgs(track_ids=["123"], artist=["Justice"]))
|
|
698
701
|
mock_query.by_track_ids.assert_called_once_with(track_ids=["123"])
|
|
699
702
|
mock_query.by_artist.assert_called_once_with("Justice")
|
|
700
703
|
mock_query.match_all.assert_not_called()
|
|
@@ -704,7 +707,7 @@ class TestGetFilteredContent:
|
|
|
704
707
|
mock_db.session = None
|
|
705
708
|
|
|
706
709
|
with pytest.raises(RuntimeError, match="No Session"):
|
|
707
|
-
get_filtered_content(mock_db)
|
|
710
|
+
get_filtered_content(mock_db, FilterArgs())
|
|
708
711
|
|
|
709
712
|
|
|
710
713
|
class TestCollectionQueryExecution:
|
|
File without changes
|
|
File without changes
|
{rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/actions/commitizen-bump/action.yml
RENAMED
|
File without changes
|
|
File without changes
|
{rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/.github/actions/install/action.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rekordbox_edit-0.4.0.dev38 → rekordbox_edit-0.4.0.dev40}/rekordbox_edit/commands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|