rekordbox-edit 0.6.0.dev11__tar.gz → 0.6.0.dev12__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 (64) hide show
  1. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/CHANGELOG.md +2 -1
  2. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/PKG-INFO +1 -1
  3. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/pyproject.toml +1 -1
  4. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/_click.py +10 -0
  5. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/cli/_utils.py +49 -1
  6. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/cli/convert.py +3 -23
  7. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/cli/edit.py +3 -4
  8. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/cli/search.py +3 -3
  9. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/cli/test_convert.py +32 -36
  10. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/cli/test_edit.py +12 -12
  11. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/cli/test_search.py +4 -4
  12. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/uv.lock +1 -1
  13. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.agent-style/RULES.md +0 -0
  14. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.agent-style/claude-code.md +0 -0
  15. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/actions/build-release-notes/action.yml +0 -0
  16. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/actions/commitizen-bump/action.yml +0 -0
  17. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/actions/commitizen-bump/commitizen-bump.sh +0 -0
  18. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/actions/install/action.yml +0 -0
  19. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/actions/lint/action.yml +0 -0
  20. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/actions/test/action.yml +0 -0
  21. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/workflows/cd.yml +0 -0
  22. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/workflows/ci.yml +0 -0
  23. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/workflows/publish.yml +0 -0
  24. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.github/workflows/release.yml +0 -0
  25. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.gitignore +0 -0
  26. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.pre-commit-config.yaml +0 -0
  27. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/.python-version +0 -0
  28. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/AGENTS.md +0 -0
  29. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/CLAUDE.md +0 -0
  30. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/CONTRIBUTING.md +0 -0
  31. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/LICENSE +0 -0
  32. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/Makefile +0 -0
  33. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/README.md +0 -0
  34. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/codecov.yml +0 -0
  35. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/__init__.py +0 -0
  36. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/api/__init__.py +0 -0
  37. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/api/_utils.py +0 -0
  38. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/api/convert.py +0 -0
  39. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/api/edit.py +0 -0
  40. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/api/search.py +0 -0
  41. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/cli/__init__.py +0 -0
  42. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/cli/main.py +0 -0
  43. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/display.py +0 -0
  44. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/logger.py +0 -0
  45. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/models.py +0 -0
  46. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/query.py +0 -0
  47. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/rekordbox_edit/utils.py +0 -0
  48. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/renovate.json5 +0 -0
  49. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/ruff.toml +0 -0
  50. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/__init__.py +0 -0
  51. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/api/__init__.py +0 -0
  52. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/api/test_convert.py +0 -0
  53. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/api/test_edit.py +0 -0
  54. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/api/test_search.py +0 -0
  55. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/api/test_utils.py +0 -0
  56. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/cli/__init__.py +0 -0
  57. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/cli/test_main.py +0 -0
  58. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/cli/test_utils.py +0 -0
  59. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/conftest.py +0 -0
  60. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/test_display.py +0 -0
  61. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/test_logger.py +0 -0
  62. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/test_models.py +0 -0
  63. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/test_query.py +0 -0
  64. {rekordbox_edit-0.6.0.dev11 → rekordbox_edit-0.6.0.dev12}/tests/test_utils.py +0 -0
@@ -1,6 +1,7 @@
1
- ## v0.6.0.dev11 (2026-06-09)
1
+ ## v0.6.0.dev12 (2026-06-09)
2
2
 
3
3
 
4
+ - feat: adds global --database-path argument
4
5
  - chore(deps): update linters to v0.0.44 (#79)
5
6
  - Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
6
7
  - test: restore coverage lost during api/cli redesign
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rekordbox-edit
3
- Version: 0.6.0.dev11
3
+ Version: 0.6.0.dev12
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "rekordbox-edit"
7
- version = "0.6.0.dev11"
7
+ version = "0.6.0.dev12"
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"
@@ -1,4 +1,5 @@
1
1
  from enum import Enum
2
+ from pathlib import Path
2
3
 
3
4
  import click
4
5
 
@@ -163,6 +164,15 @@ convert_click_options = [
163
164
  ]
164
165
 
165
166
 
167
+ database_path_option = click.option(
168
+ "--database-path",
169
+ type=click.Path(exists=True, path_type=Path),
170
+ default=None,
171
+ envvar="RBE_DATABASE_PATH",
172
+ help="Path to master.db. Bypasses Rekordbox installation discovery.",
173
+ )
174
+
175
+
166
176
  def add_click_options(options):
167
177
  def _add_options(func):
168
178
  for option in reversed(options):
@@ -1,13 +1,17 @@
1
1
  """CLI-private helpers: stdin handling, scripting guards, args narrowing, print emitters."""
2
2
 
3
+ import functools
3
4
  import logging
4
5
  import sys
5
6
  from copy import copy
6
7
 
7
8
  import click
8
9
  from pydantic import BaseModel
10
+ from pyrekordbox import Rekordbox6Database
11
+ from pyrekordbox.utils import get_rekordbox_pid
9
12
 
10
- from rekordbox_edit._click import PrintChoice
13
+ from rekordbox_edit._click import PrintChoice, database_path_option
14
+ from rekordbox_edit.utils import UserQuit, confirm
11
15
 
12
16
  logger = logging.getLogger(__name__)
13
17
 
@@ -77,3 +81,47 @@ def _print_response_ids(response) -> None:
77
81
  def _print_response_json(response: BaseModel) -> None:
78
82
  """Print the response envelope as JSON."""
79
83
  print(response.model_dump_json())
84
+
85
+
86
+ def _rekordbox_running_confirm(print_opt) -> bool:
87
+ rekordbox_pid = get_rekordbox_pid()
88
+ if not rekordbox_pid:
89
+ return True
90
+ if print_opt in SCRIPTING_MODES:
91
+ logger.error(
92
+ f"Rekordbox is running (PID {rekordbox_pid}). Cannot proceed in scripting mode."
93
+ )
94
+ sys.exit(1)
95
+ logger.warning(
96
+ f"Rekordbox is running (PID {rekordbox_pid}). Modifying the database while "
97
+ "Rekordbox is open can cause conflicts."
98
+ )
99
+ try:
100
+ return confirm("Continue anyway?", default=False)
101
+ except UserQuit:
102
+ return False
103
+
104
+
105
+ def with_database(*, writes: bool = False):
106
+ """Inject an opened Rekordbox6Database as `db` and close it on exit.
107
+
108
+ Pass writes=True for commands that modify the DB: the wrapper aborts when
109
+ Rekordbox is running (or prompts to continue in interactive modes).
110
+ """
111
+
112
+ def decorator(func):
113
+ @database_path_option
114
+ @functools.wraps(func)
115
+ def wrapper(**kwargs):
116
+ if writes and not _rekordbox_running_confirm(kwargs.get("print_opt")):
117
+ return
118
+ database_path: str | None = kwargs.pop("database_path", None)
119
+ db = Rekordbox6Database(path=database_path) # ty: ignore[invalid-argument-type]
120
+ try:
121
+ return func(db=db, **kwargs)
122
+ finally:
123
+ db.close()
124
+
125
+ return wrapper
126
+
127
+ return decorator
@@ -1,11 +1,8 @@
1
1
  """Convert CLI command."""
2
2
 
3
3
  import logging
4
- import sys
5
4
 
6
5
  import click
7
- from pyrekordbox import Rekordbox6Database
8
- from pyrekordbox.utils import get_rekordbox_pid
9
6
 
10
7
  from rekordbox_edit._click import (
11
8
  PrintChoice,
@@ -24,6 +21,7 @@ from rekordbox_edit.cli._utils import (
24
21
  _print_response_ids,
25
22
  _print_response_json,
26
23
  _validate_scripting_preconditions,
24
+ with_database,
27
25
  )
28
26
  from rekordbox_edit.display import PrintableField, print_track_info
29
27
  from rekordbox_edit.logger import get_debug_file_path, set_level
@@ -45,7 +43,8 @@ logger = logging.getLogger(__name__)
45
43
  track_ids_argument,
46
44
  ]
47
45
  )
48
- def convert_command(**kwargs):
46
+ @with_database(writes=True)
47
+ def convert_command(db, **kwargs):
49
48
  """Convert lossless audio files between formats and update RekordBox database.
50
49
 
51
50
  Supports conversion from any lossless format (FLAC, AIFF, WAV) to:
@@ -67,25 +66,6 @@ def convert_command(**kwargs):
67
66
 
68
67
  scripting_mode = print_opt in SCRIPTING_MODES
69
68
 
70
- rekordbox_pid = get_rekordbox_pid()
71
- if rekordbox_pid:
72
- if scripting_mode:
73
- logger.error(
74
- f"Rekordbox is running (PID {rekordbox_pid}). Cannot proceed in scripting mode."
75
- )
76
- sys.exit(1)
77
- logger.warning(
78
- f"Rekordbox is running (PID {rekordbox_pid}). Modifying the database while "
79
- "Rekordbox is open can cause conflicts."
80
- )
81
- try:
82
- if not confirm("Continue anyway?", default=False):
83
- return
84
- except UserQuit:
85
- return
86
-
87
- db = Rekordbox6Database()
88
-
89
69
  if yes or dry_run:
90
70
  response = convert(db, args, dry_run=dry_run)
91
71
  _report_skips(response.result.skipped)
@@ -3,7 +3,6 @@
3
3
  import logging
4
4
 
5
5
  import click
6
- from pyrekordbox import Rekordbox6Database
7
6
 
8
7
  from rekordbox_edit._click import (
9
8
  PrintChoice,
@@ -22,6 +21,7 @@ from rekordbox_edit.cli._utils import (
22
21
  _print_response_ids,
23
22
  _print_response_json,
24
23
  _validate_scripting_preconditions,
24
+ with_database,
25
25
  )
26
26
  from rekordbox_edit.display import PrintableField, print_track_info
27
27
  from rekordbox_edit.logger import get_debug_file_path, set_level
@@ -47,7 +47,8 @@ logger = logging.getLogger(__name__)
47
47
  "field",
48
48
  type=click.Choice(list(FIELD_COLUMNS.keys()), case_sensitive=False),
49
49
  )
50
- def edit_command(**kwargs):
50
+ @with_database(writes=True)
51
+ def edit_command(db, **kwargs):
51
52
  """Edit a metadata field on tracks in the RekordBox database."""
52
53
  print_opt = kwargs.pop("print_opt", None)
53
54
  # CLI-only flags pulled out of kwargs before constructing EditArgs.
@@ -65,8 +66,6 @@ def edit_command(**kwargs):
65
66
  piped_stdin,
66
67
  )
67
68
 
68
- db = Rekordbox6Database()
69
-
70
69
  if yes or dry_run:
71
70
  try:
72
71
  response = edit(db, args, dry_run=dry_run)
@@ -3,7 +3,6 @@
3
3
  import logging
4
4
 
5
5
  import click
6
- from pyrekordbox import Rekordbox6Database
7
6
 
8
7
  from rekordbox_edit._click import (
9
8
  PrintChoice,
@@ -17,6 +16,7 @@ from rekordbox_edit.cli._utils import (
17
16
  _handle_stdin,
18
17
  _print_response_ids,
19
18
  _print_response_json,
19
+ with_database,
20
20
  )
21
21
  from rekordbox_edit.display import print_track_info
22
22
  from rekordbox_edit.logger import get_debug_file_path, set_level
@@ -29,14 +29,14 @@ logger = logging.getLogger(__name__)
29
29
  epilog=f"Debug logs for each run can be found at:\n{get_debug_file_path().parent}"
30
30
  )
31
31
  @add_click_options([*global_click_filters, print_option, track_ids_argument])
32
- def search_command(**kwargs):
32
+ @with_database()
33
+ def search_command(db, **kwargs):
33
34
  """Search the RekordBox database."""
34
35
  print_opt = kwargs.pop("print_opt", None)
35
36
  args = SearchArgs(**kwargs)
36
37
  set_level(print_opt)
37
38
  _handle_stdin(args)
38
39
 
39
- db = Rekordbox6Database()
40
40
  response = search(db, args)
41
41
 
42
42
  if print_opt is PrintChoice.SILENT:
@@ -40,8 +40,8 @@ def _response(tracks=None, converted=None, deleted=0, skipped=None, format_out="
40
40
 
41
41
  class TestConvertCommand:
42
42
  @patch("rekordbox_edit.cli.convert.convert")
43
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
44
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
43
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
44
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
45
45
  def test_yes_calls_convert_once(self, _pid, mock_db_class, mock_convert):
46
46
  mock_db_class.return_value = Mock(session=Mock())
47
47
  mock_convert.return_value = _response()
@@ -52,8 +52,8 @@ class TestConvertCommand:
52
52
  mock_convert.assert_called_once()
53
53
 
54
54
  @patch("rekordbox_edit.cli.convert.convert")
55
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
56
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
55
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
56
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
57
57
  def test_dry_run(self, _pid, mock_db_class, mock_convert):
58
58
  mock_db_class.return_value = Mock(session=Mock())
59
59
  mock_convert.return_value = _response()
@@ -67,8 +67,8 @@ class TestConvertCommand:
67
67
  assert mock_convert.call_args.kwargs.get("dry_run") is True
68
68
 
69
69
  @patch("rekordbox_edit.cli.convert.convert")
70
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
71
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
70
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
71
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
72
72
  def test_warns_already_target_skip(
73
73
  self, _pid, mock_db_class, mock_convert, mock_logger
74
74
  ):
@@ -84,8 +84,8 @@ class TestConvertCommand:
84
84
  assert not any("--overwrite" in w for w in warnings)
85
85
 
86
86
  @patch("rekordbox_edit.cli.convert.convert")
87
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
88
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
87
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
88
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
89
89
  def test_warns_output_conflict_skip(
90
90
  self, _pid, mock_db_class, mock_convert, mock_logger
91
91
  ):
@@ -100,8 +100,8 @@ class TestConvertCommand:
100
100
  assert any("--overwrite" in w for w in warnings)
101
101
 
102
102
  @patch("rekordbox_edit.cli.convert.convert")
103
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
104
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
103
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
104
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
105
105
  def test_logs_deleted_count(self, _pid, mock_db_class, mock_convert, mock_logger):
106
106
  mock_db_class.return_value = Mock(session=Mock())
107
107
  mock_convert.return_value = _response(deleted=2)
@@ -110,13 +110,10 @@ class TestConvertCommand:
110
110
 
111
111
  mock_logger.info.assert_any_call("Deleted 2 original file(s)")
112
112
 
113
- @patch("rekordbox_edit.cli.convert._handle_stdin", return_value=False)
114
- @patch("rekordbox_edit.cli.convert.confirm")
115
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
116
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=12345)
117
- def test_rekordbox_running_user_declines(
118
- self, _pid, mock_db_class, mock_confirm, _stdin
119
- ):
113
+ @patch("rekordbox_edit.cli._utils.confirm")
114
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
115
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=12345)
116
+ def test_rekordbox_running_user_declines(self, _pid, mock_db_class, mock_confirm):
120
117
  mock_confirm.return_value = False
121
118
 
122
119
  result = CliRunner().invoke(convert_command, ["--format-out", "aiff"])
@@ -124,9 +121,9 @@ class TestConvertCommand:
124
121
  assert result.exit_code == 0
125
122
  mock_db_class.assert_not_called()
126
123
 
127
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
124
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
128
125
  def test_aborts_in_scripting_mode_when_rekordbox_running(self, mock_db_class):
129
- with patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=12345):
126
+ with patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=12345):
130
127
  result = CliRunner().invoke(
131
128
  convert_command,
132
129
  ["--format-out", "aiff", "--yes", "--print", "silent"],
@@ -135,8 +132,8 @@ class TestConvertCommand:
135
132
  assert result.exit_code != 0
136
133
 
137
134
  @patch("rekordbox_edit.cli.convert.convert")
138
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
139
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
135
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
136
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
140
137
  def test_print_json_emits_envelope(self, _pid, mock_db_class, mock_convert):
141
138
  import json
142
139
 
@@ -154,8 +151,8 @@ class TestConvertCommand:
154
151
  @patch("rekordbox_edit.cli.convert.print_track_info")
155
152
  @patch("rekordbox_edit.cli.convert.confirm", return_value=True)
156
153
  @patch("rekordbox_edit.cli.convert.convert")
157
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
158
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
154
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
155
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
159
156
  def test_default_flow_previews_then_commits(
160
157
  self, _pid, mock_db_class, mock_convert, _confirm, _print
161
158
  ):
@@ -172,8 +169,8 @@ class TestConvertCommand:
172
169
  @patch("rekordbox_edit.cli.convert.print_track_info")
173
170
  @patch("rekordbox_edit.cli.convert.confirm", return_value=False)
174
171
  @patch("rekordbox_edit.cli.convert.convert")
175
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
176
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
172
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
173
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
177
174
  def test_default_flow_user_declines_skips_commit(
178
175
  self, _pid, mock_db_class, mock_convert, _confirm, _print
179
176
  ):
@@ -189,8 +186,8 @@ class TestConvertCommand:
189
186
  @patch("rekordbox_edit.cli.convert.print_track_info")
190
187
  @patch("rekordbox_edit.cli.convert.confirm", side_effect=UserQuit)
191
188
  @patch("rekordbox_edit.cli.convert.convert")
192
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
193
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
189
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
190
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
194
191
  def test_default_flow_user_quit_skips_commit(
195
192
  self, _pid, mock_db_class, mock_convert, _confirm, _print
196
193
  ):
@@ -203,8 +200,8 @@ class TestConvertCommand:
203
200
  mock_convert.assert_called_once() # preview only
204
201
 
205
202
  @patch("rekordbox_edit.cli.convert.convert")
206
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
207
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
203
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
204
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
208
205
  def test_default_flow_empty_preview_exits_cleanly(
209
206
  self, _pid, mock_db_class, mock_convert
210
207
  ):
@@ -224,8 +221,8 @@ class TestConvertCommand:
224
221
  @patch("rekordbox_edit.cli.convert.print_track_info")
225
222
  @patch("rekordbox_edit.cli.convert.confirm")
226
223
  @patch("rekordbox_edit.cli.convert.convert")
227
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
228
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=None)
224
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
225
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=None)
229
226
  def test_interactive_narrows_to_confirmed_tracks(
230
227
  self, _pid, mock_db_class, mock_convert, mock_confirm, _print
231
228
  ):
@@ -251,12 +248,11 @@ class TestConvertCommand:
251
248
  narrowed_args = mock_convert.call_args_list[1].args[1]
252
249
  assert narrowed_args.track_ids == ["A"]
253
250
 
254
- @patch("rekordbox_edit.cli.convert._handle_stdin", return_value=False)
255
- @patch("rekordbox_edit.cli.convert.confirm", return_value=True)
256
- @patch("rekordbox_edit.cli.convert.Rekordbox6Database")
257
- @patch("rekordbox_edit.cli.convert.get_rekordbox_pid", return_value=12345)
251
+ @patch("rekordbox_edit.cli._utils.confirm", return_value=True)
252
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
253
+ @patch("rekordbox_edit.cli._utils.get_rekordbox_pid", return_value=12345)
258
254
  def test_rekordbox_running_user_accepts_continues(
259
- self, _pid, mock_db_class, _confirm, _stdin
255
+ self, _pid, mock_db_class, _confirm
260
256
  ):
261
257
  with patch("rekordbox_edit.cli.convert.convert") as mock_convert:
262
258
  mock_db_class.return_value = Mock(session=Mock())
@@ -29,7 +29,7 @@ def _response(tracks=None, edits=None, skipped=None):
29
29
 
30
30
  class TestEditCommand:
31
31
  @patch("rekordbox_edit.cli.edit.edit")
32
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
32
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
33
33
  def test_yes_calls_edit_once(self, mock_db_class, mock_edit):
34
34
  mock_db_class.return_value = Mock(session=Mock())
35
35
  mock_edit.return_value = _response()
@@ -44,7 +44,7 @@ class TestEditCommand:
44
44
  assert mock_edit.call_args.kwargs.get("dry_run", False) is False
45
45
 
46
46
  @patch("rekordbox_edit.cli.edit.edit")
47
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
47
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
48
48
  def test_dry_run_passes_flag_through(self, mock_db_class, mock_edit):
49
49
  mock_db_class.return_value = Mock(session=Mock())
50
50
  mock_edit.return_value = _response()
@@ -58,7 +58,7 @@ class TestEditCommand:
58
58
  assert mock_edit.call_args.kwargs.get("dry_run") is True
59
59
 
60
60
  @patch("rekordbox_edit.cli.edit.edit")
61
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
61
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
62
62
  def test_empty_result_exits_cleanly(self, mock_db_class, mock_edit):
63
63
  mock_db_class.return_value = Mock(session=Mock())
64
64
  mock_edit.return_value = _response(tracks=[], edits=[])
@@ -70,7 +70,7 @@ class TestEditCommand:
70
70
  assert result.exit_code == 0
71
71
 
72
72
  @patch("rekordbox_edit.cli.edit.edit")
73
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
73
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
74
74
  def test_value_error_becomes_usage_error(self, mock_db_class, mock_edit):
75
75
  mock_db_class.return_value = Mock(session=Mock())
76
76
  mock_edit.side_effect = ValueError("Found 2 tracks that would be edited")
@@ -83,7 +83,7 @@ class TestEditCommand:
83
83
  assert "Error" in result.output
84
84
 
85
85
  @patch("rekordbox_edit.cli.edit.edit")
86
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
86
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
87
87
  def test_print_ids_outputs_ids(self, mock_db_class, mock_edit):
88
88
  mock_db_class.return_value = Mock(session=Mock())
89
89
  track = Track(ID="AAA", FileNameL="x.wav", FolderPath="/x.wav")
@@ -99,7 +99,7 @@ class TestEditCommand:
99
99
  assert "AAA" in result.output
100
100
 
101
101
  @patch("rekordbox_edit.cli.edit.edit")
102
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
102
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
103
103
  def test_print_json_outputs_envelope(self, mock_db_class, mock_edit):
104
104
  import json
105
105
 
@@ -121,7 +121,7 @@ class TestEditCommand:
121
121
  @patch("rekordbox_edit.cli.edit.print_track_info")
122
122
  @patch("rekordbox_edit.cli.edit.confirm", return_value=True)
123
123
  @patch("rekordbox_edit.cli.edit.edit")
124
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
124
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
125
125
  def test_default_flow_previews_then_commits(
126
126
  self, mock_db_class, mock_edit, mock_confirm, _print
127
127
  ):
@@ -138,7 +138,7 @@ class TestEditCommand:
138
138
  @patch("rekordbox_edit.cli.edit.print_track_info")
139
139
  @patch("rekordbox_edit.cli.edit.confirm", return_value=False)
140
140
  @patch("rekordbox_edit.cli.edit.edit")
141
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
141
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
142
142
  def test_default_flow_user_declines_skips_commit(
143
143
  self, mock_db_class, mock_edit, mock_confirm, _print
144
144
  ):
@@ -154,7 +154,7 @@ class TestEditCommand:
154
154
  @patch("rekordbox_edit.cli.edit.confirm", side_effect=UserQuit)
155
155
  @patch("rekordbox_edit.cli.edit.print_track_info")
156
156
  @patch("rekordbox_edit.cli.edit.edit")
157
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
157
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
158
158
  def test_default_flow_user_quit_skips_commit(
159
159
  self, mock_db_class, mock_edit, _print, _confirm
160
160
  ):
@@ -168,7 +168,7 @@ class TestEditCommand:
168
168
 
169
169
  @patch("rekordbox_edit.cli.edit.print_track_info")
170
170
  @patch("rekordbox_edit.cli.edit.edit")
171
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
171
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
172
172
  def test_default_flow_empty_preview_exits_cleanly(
173
173
  self, mock_db_class, mock_edit, _print
174
174
  ):
@@ -186,7 +186,7 @@ class TestEditCommand:
186
186
  @patch("rekordbox_edit.cli.edit.print_track_info")
187
187
  @patch("rekordbox_edit.cli.edit.confirm")
188
188
  @patch("rekordbox_edit.cli.edit.edit")
189
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
189
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
190
190
  def test_interactive_narrows_to_confirmed_tracks(
191
191
  self, mock_db_class, mock_edit, mock_confirm, _print
192
192
  ):
@@ -215,7 +215,7 @@ class TestEditCommand:
215
215
  @patch("rekordbox_edit.cli.edit.print_track_info")
216
216
  @patch("rekordbox_edit.cli.edit.confirm", side_effect=UserQuit)
217
217
  @patch("rekordbox_edit.cli.edit.edit")
218
- @patch("rekordbox_edit.cli.edit.Rekordbox6Database")
218
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
219
219
  def test_interactive_user_quit_skips_commit(
220
220
  self, mock_db_class, mock_edit, _confirm, _print
221
221
  ):
@@ -22,7 +22,7 @@ def _response(*tracks):
22
22
  class TestSearchCommand:
23
23
  @patch("rekordbox_edit.cli.search.print_track_info")
24
24
  @patch("rekordbox_edit.cli.search.search")
25
- @patch("rekordbox_edit.cli.search.Rekordbox6Database")
25
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
26
26
  def test_calls_print_track_info_by_default(
27
27
  self, mock_db_class, mock_search, mock_print, make_track
28
28
  ):
@@ -35,7 +35,7 @@ class TestSearchCommand:
35
35
  mock_print.assert_called_once()
36
36
 
37
37
  @patch("rekordbox_edit.cli.search.search")
38
- @patch("rekordbox_edit.cli.search.Rekordbox6Database")
38
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
39
39
  def test_print_ids(self, mock_db_class, mock_search, make_track):
40
40
  mock_db_class.return_value = Mock(session=Mock())
41
41
  mock_search.return_value = _response(make_track(ID="A"), make_track(ID="B"))
@@ -46,7 +46,7 @@ class TestSearchCommand:
46
46
  assert "A B" in result.output
47
47
 
48
48
  @patch("rekordbox_edit.cli.search.search")
49
- @patch("rekordbox_edit.cli.search.Rekordbox6Database")
49
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
50
50
  def test_print_json_emits_envelope(self, mock_db_class, mock_search, make_track):
51
51
  import json
52
52
 
@@ -60,7 +60,7 @@ class TestSearchCommand:
60
60
  assert payload["tracks"][0]["ID"] == "A"
61
61
 
62
62
  @patch("rekordbox_edit.cli.search.search")
63
- @patch("rekordbox_edit.cli.search.Rekordbox6Database")
63
+ @patch("rekordbox_edit.cli._utils.Rekordbox6Database")
64
64
  def test_print_silent_produces_no_output(
65
65
  self, mock_db_class, mock_search, make_track
66
66
  ):
@@ -990,7 +990,7 @@ wheels = [
990
990
 
991
991
  [[package]]
992
992
  name = "rekordbox-edit"
993
- version = "0.6.0.dev11"
993
+ version = "0.6.0.dev12"
994
994
  source = { editable = "." }
995
995
  dependencies = [
996
996
  { name = "click" },