gukebox 1.0.0.dev12__tar.gz → 1.0.0.dev14__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.
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/PKG-INFO +19 -21
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/README.md +10 -7
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/api/current_tag_router.py +6 -6
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/api/discs_router.py +2 -4
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/api/models.py +11 -11
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/api/settings_router.py +5 -5
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/api_controller.py +3 -5
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/cli_controller.py +19 -21
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/cli_display.py +2 -4
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/interactive_cli_controller.py +20 -20
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/ui_controller.py +44 -50
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/ui_pages/library.py +16 -16
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/ui_pages/settings.py +12 -10
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/ui_pages/sonos.py +26 -27
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/command_handlers.py +2 -1
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/commands.py +12 -12
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/use_cases/edit_disc.py +3 -5
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/use_cases/get_current_tag_status.py +1 -3
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/use_cases/list_discs.py +1 -3
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/use_cases/resolve_tag_id.py +1 -3
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/use_cases/search_discs.py +1 -3
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/inbound/config.py +11 -11
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/json_library_adapter.py +9 -8
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/players/dryrun_player_adapter.py +1 -1
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/players/sonos_player_adapter.py +38 -24
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/readers/dryrun_reader_adapter.py +5 -5
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/readers/pn532_reader_adapter.py +5 -8
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/sonos_discovery_adapter.py +7 -7
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/text_current_tag_adapter.py +4 -5
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/app.py +27 -27
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/cli_presentation.py +43 -46
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/command_handlers.py +11 -10
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/commands.py +17 -17
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/di_container.py +4 -6
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/pn532_command_handlers.py +8 -7
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/pn532_commands.py +2 -2
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/di_container.py +29 -27
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/entities/current_tag_action.py +2 -2
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/entities/disc.py +4 -6
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/entities/library.py +1 -3
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/entities/playback_action.py +2 -2
- gukebox-1.0.0.dev14/jukebox/domain/entities/playback_session.py +17 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/entities/tag_event.py +1 -3
- gukebox-1.0.0.dev14/jukebox/domain/errors.py +2 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/ports/reader_port.py +1 -2
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/repositories/current_tag_repository.py +1 -2
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/repositories/library_repository.py +1 -2
- gukebox-1.0.0.dev14/jukebox/domain/use_cases/handle_tag_event.py +145 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/pn532/profiles.py +8 -8
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/definitions.py +3 -2
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/entities.py +50 -50
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/file_settings_repository.py +2 -3
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/migration.py +1 -2
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/resolve.py +6 -6
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/runtime_resolver.py +2 -2
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/selected_sonos_group_repository.py +2 -4
- gukebox-1.0.0.dev14/jukebox/settings/types.py +4 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/validation_rules.py +3 -2
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/view_utils.py +2 -2
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/sonos/selection.py +8 -8
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/sonos/service.py +3 -3
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/pyproject.toml +15 -25
- gukebox-1.0.0.dev12/jukebox/domain/entities/playback_session.py +0 -19
- gukebox-1.0.0.dev12/jukebox/domain/use_cases/handle_tag_event.py +0 -121
- gukebox-1.0.0.dev12/jukebox/settings/types.py +0 -4
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/LICENSE +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/api/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/ui_pages/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/outbound/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/outbound/json_library_adapter.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/outbound/text_current_tag_adapter.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/di_container.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/entities/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/entities/current_tag_status.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/repositories/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/use_cases/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/use_cases/add_disc.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/use_cases/get_disc.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/domain/use_cases/remove_disc.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/inbound/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/inbound/cli_controller.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/players/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/adapters/outbound/readers/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/services.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/admin/sonos_households.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/app.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/entities/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/ports/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/ports/player_port.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/repositories/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/use_cases/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/use_cases/determine_action.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/domain/use_cases/determine_current_tag_action.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/pn532/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/dict_utils.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/errors.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/repositories.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/runtime_validation.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/service_protocols.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/settings/timing_validation.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/shared/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/shared/config_utils.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/shared/dependency_messages.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/shared/logger.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/shared/terminal_ui.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/shared/timing.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/sonos/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/jukebox/sonos/discovery.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/pn532/__init__.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/pn532/pn532.py +0 -0
- {gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/pn532/spi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: gukebox
|
|
3
|
-
Version: 1.0.0.
|
|
3
|
+
Version: 1.0.0.dev14
|
|
4
4
|
Summary: A Jukebox to play music on speakers using 'CD' with NFC tag
|
|
5
5
|
Keywords: jukebox,music,nfc
|
|
6
6
|
Author: Gudsfile
|
|
@@ -29,27 +29,22 @@ Classifier: Development Status :: 4 - Beta
|
|
|
29
29
|
Classifier: License :: OSI Approved :: MIT License
|
|
30
30
|
Classifier: Operating System :: OS Independent
|
|
31
31
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
32
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
33
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
34
32
|
Classifier: Programming Language :: Python :: 3.11
|
|
35
33
|
Classifier: Programming Language :: Python :: 3.12
|
|
36
34
|
Classifier: Programming Language :: Python :: 3.13
|
|
37
|
-
Requires-Dist: pydantic==2.
|
|
35
|
+
Requires-Dist: pydantic==2.13.3
|
|
38
36
|
Requires-Dist: questionary==2.1.1
|
|
39
|
-
Requires-Dist: soco==0.
|
|
40
|
-
Requires-Dist: typer==0.
|
|
41
|
-
Requires-Dist:
|
|
42
|
-
Requires-Dist:
|
|
43
|
-
Requires-Dist: fastapi==0.135.1 ; python_full_version >= '3.10' and extra == 'api'
|
|
44
|
-
Requires-Dist: uvicorn==0.39.0 ; python_full_version < '3.10' and extra == 'api'
|
|
45
|
-
Requires-Dist: uvicorn==0.41.0 ; python_full_version >= '3.10' and extra == 'api'
|
|
37
|
+
Requires-Dist: soco==0.31.0
|
|
38
|
+
Requires-Dist: typer==0.24.1
|
|
39
|
+
Requires-Dist: fastapi==0.135.1 ; extra == 'api'
|
|
40
|
+
Requires-Dist: uvicorn==0.41.0 ; extra == 'api'
|
|
46
41
|
Requires-Dist: pyserial==3.5 ; extra == 'pn532'
|
|
47
42
|
Requires-Dist: spidev==3.8 ; extra == 'pn532'
|
|
48
43
|
Requires-Dist: lgpio==0.2.2.0 ; python_full_version < '3.13' and extra == 'pn532'
|
|
49
44
|
Requires-Dist: gukebox[api] ; extra == 'ui'
|
|
50
|
-
Requires-Dist: fastui==0.9.0 ;
|
|
51
|
-
Requires-Dist: python-multipart==0.0.
|
|
52
|
-
Requires-Python: >=3.
|
|
45
|
+
Requires-Dist: fastui==0.9.0 ; extra == 'ui'
|
|
46
|
+
Requires-Dist: python-multipart==0.0.26 ; extra == 'ui'
|
|
47
|
+
Requires-Python: >=3.11, <3.14
|
|
53
48
|
Project-URL: Repository, https://github.com/Gudsfile/jukebox
|
|
54
49
|
Provides-Extra: api
|
|
55
50
|
Provides-Extra: pn532
|
|
@@ -91,13 +86,17 @@ Description-Content-Type: text/markdown
|
|
|
91
86
|
- [The library file](#the-library-file)
|
|
92
87
|
- [Developer setup](#developer-setup)
|
|
93
88
|
|
|
94
|
-
##
|
|
89
|
+
## Python Compatibility
|
|
95
90
|
|
|
96
|
-
|
|
91
|
+
Jukebox 1.0+ requires Python 3.11 or newer.
|
|
97
92
|
|
|
98
|
-
Python
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
| Python version | Compatible Jukebox versions | Notes |
|
|
94
|
+
|----------------|-----------------------------|-------|
|
|
95
|
+
| 3.7 | 0.4.0 – 0.4.1 | Legacy |
|
|
96
|
+
| 3.8 | 0.4.0 – 0.5.4 | Legacy |
|
|
97
|
+
| 3.9 – 3.10 | 0.4.0 – 0.9.0 (incl. 1.0.0.dev13) | Legacy |
|
|
98
|
+
| 3.11 – 3.12 | 0.4.0 – latest | Actively supported |
|
|
99
|
+
| 3.13 | 0.5.3 – latest | Actively supported (see installation notes) |
|
|
101
100
|
|
|
102
101
|
## Install
|
|
103
102
|
|
|
@@ -190,10 +189,9 @@ jukebox-admin settings show --effective
|
|
|
190
189
|
To use the `api` and `ui` commands, additional packages are required. You can install the `package[extra]` syntax regardless of the package manager you use, for example:
|
|
191
190
|
|
|
192
191
|
```shell
|
|
193
|
-
# Python 3.9+ required
|
|
194
192
|
uv tool install gukebox[api]
|
|
195
193
|
|
|
196
|
-
#
|
|
194
|
+
# ui includes the api extra
|
|
197
195
|
uv tool install gukebox[ui]
|
|
198
196
|
```
|
|
199
197
|
|
|
@@ -33,13 +33,17 @@
|
|
|
33
33
|
- [The library file](#the-library-file)
|
|
34
34
|
- [Developer setup](#developer-setup)
|
|
35
35
|
|
|
36
|
-
##
|
|
36
|
+
## Python Compatibility
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Jukebox 1.0+ requires Python 3.11 or newer.
|
|
39
39
|
|
|
40
|
-
Python
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
| Python version | Compatible Jukebox versions | Notes |
|
|
41
|
+
|----------------|-----------------------------|-------|
|
|
42
|
+
| 3.7 | 0.4.0 – 0.4.1 | Legacy |
|
|
43
|
+
| 3.8 | 0.4.0 – 0.5.4 | Legacy |
|
|
44
|
+
| 3.9 – 3.10 | 0.4.0 – 0.9.0 (incl. 1.0.0.dev13) | Legacy |
|
|
45
|
+
| 3.11 – 3.12 | 0.4.0 – latest | Actively supported |
|
|
46
|
+
| 3.13 | 0.5.3 – latest | Actively supported (see installation notes) |
|
|
43
47
|
|
|
44
48
|
## Install
|
|
45
49
|
|
|
@@ -132,10 +136,9 @@ jukebox-admin settings show --effective
|
|
|
132
136
|
To use the `api` and `ui` commands, additional packages are required. You can install the `package[extra]` syntax regardless of the package manager you use, for example:
|
|
133
137
|
|
|
134
138
|
```shell
|
|
135
|
-
# Python 3.9+ required
|
|
136
139
|
uv tool install gukebox[api]
|
|
137
140
|
|
|
138
|
-
#
|
|
141
|
+
# ui includes the api extra
|
|
139
142
|
uv tool install gukebox[ui]
|
|
140
143
|
```
|
|
141
144
|
|
{gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/api/current_tag_router.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
3
|
from fastapi import APIRouter, HTTPException, Response, status
|
|
4
4
|
from pydantic import ValidationError
|
|
@@ -27,11 +27,11 @@ def build_current_tag_router(
|
|
|
27
27
|
) -> APIRouter:
|
|
28
28
|
router = APIRouter(prefix="/api/v1", tags=["current-tag"])
|
|
29
29
|
|
|
30
|
-
def read_current_tag_status() ->
|
|
30
|
+
def read_current_tag_status() -> CurrentTagStatus | None:
|
|
31
31
|
return get_current_tag_status.execute()
|
|
32
32
|
|
|
33
33
|
def ensure_expected_tag_id_matches(
|
|
34
|
-
expected_tag_id:
|
|
34
|
+
expected_tag_id: str | None, current_tag_status: CurrentTagStatus | None
|
|
35
35
|
) -> None:
|
|
36
36
|
if expected_tag_id is None:
|
|
37
37
|
return
|
|
@@ -84,7 +84,7 @@ def build_current_tag_router(
|
|
|
84
84
|
)
|
|
85
85
|
def create_current_tag_disc(
|
|
86
86
|
disc: DiscInput,
|
|
87
|
-
expected_tag_id:
|
|
87
|
+
expected_tag_id: str | None = None,
|
|
88
88
|
) -> Any:
|
|
89
89
|
current_tag_status = read_current_tag_status()
|
|
90
90
|
ensure_expected_tag_id_matches(expected_tag_id, current_tag_status)
|
|
@@ -112,7 +112,7 @@ def build_current_tag_router(
|
|
|
112
112
|
)
|
|
113
113
|
def update_current_tag_disc(
|
|
114
114
|
disc_patch: DiscPatchInput,
|
|
115
|
-
expected_tag_id:
|
|
115
|
+
expected_tag_id: str | None = None,
|
|
116
116
|
) -> Any:
|
|
117
117
|
current_tag_status = read_current_tag_status()
|
|
118
118
|
ensure_expected_tag_id_matches(expected_tag_id, current_tag_status)
|
|
@@ -147,7 +147,7 @@ def build_current_tag_router(
|
|
|
147
147
|
},
|
|
148
148
|
summary="Delete the current tag disc",
|
|
149
149
|
)
|
|
150
|
-
def delete_current_tag_disc(expected_tag_id:
|
|
150
|
+
def delete_current_tag_disc(expected_tag_id: str | None = None) -> Response:
|
|
151
151
|
current_tag_status = read_current_tag_status()
|
|
152
152
|
ensure_expected_tag_id_matches(expected_tag_id, current_tag_status)
|
|
153
153
|
if current_tag_status is None:
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Dict
|
|
2
|
-
|
|
3
1
|
from fastapi import APIRouter, HTTPException, Response, status
|
|
4
2
|
from pydantic import ValidationError
|
|
5
3
|
|
|
@@ -21,8 +19,8 @@ def build_discs_router(
|
|
|
21
19
|
) -> APIRouter:
|
|
22
20
|
router = APIRouter(prefix="/api/v1", tags=["discs"])
|
|
23
21
|
|
|
24
|
-
@router.get("/discs", response_model=
|
|
25
|
-
def list_discs_route() ->
|
|
22
|
+
@router.get("/discs", response_model=dict[str, DiscOutput], summary="List discs")
|
|
23
|
+
def list_discs_route() -> dict[str, Disc]:
|
|
26
24
|
return list_discs.execute()
|
|
27
25
|
|
|
28
26
|
@router.get("/discs/{tag_id}", response_model=DiscOutput, summary="Get a disc")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, RootModel
|
|
4
4
|
|
|
@@ -14,21 +14,21 @@ class DiscOutput(Disc):
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class DiscPatchMetadataInput(BaseModel):
|
|
17
|
-
artist:
|
|
18
|
-
album:
|
|
19
|
-
track:
|
|
20
|
-
playlist:
|
|
17
|
+
artist: str | None = None
|
|
18
|
+
album: str | None = None
|
|
19
|
+
track: str | None = None
|
|
20
|
+
playlist: str | None = None
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class DiscPatchOptionInput(BaseModel):
|
|
24
|
-
shuffle:
|
|
25
|
-
is_test:
|
|
24
|
+
shuffle: bool | None = None
|
|
25
|
+
is_test: bool | None = None
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class DiscPatchInput(BaseModel):
|
|
29
|
-
uri:
|
|
30
|
-
metadata:
|
|
31
|
-
option:
|
|
29
|
+
uri: str | None = None
|
|
30
|
+
metadata: DiscPatchMetadataInput | None = None
|
|
31
|
+
option: DiscPatchOptionInput | None = None
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
class CurrentTagStatusOutput(CurrentTagStatus):
|
|
@@ -44,5 +44,5 @@ class SettingsResetInput(BaseModel):
|
|
|
44
44
|
path: str
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
class SettingsPatchInput(RootModel[
|
|
47
|
+
class SettingsPatchInput(RootModel[dict[str, Any]]):
|
|
48
48
|
pass
|
{gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/api/settings_router.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any,
|
|
1
|
+
from typing import Any, cast
|
|
2
2
|
|
|
3
3
|
from fastapi import APIRouter, HTTPException
|
|
4
4
|
|
|
@@ -11,21 +11,21 @@ from jukebox.settings.types import JsonObject
|
|
|
11
11
|
def build_settings_router(settings_service: SettingsService) -> APIRouter:
|
|
12
12
|
router = APIRouter(prefix="/api/v1", tags=["settings"])
|
|
13
13
|
|
|
14
|
-
@router.get("/settings", response_model=
|
|
14
|
+
@router.get("/settings", response_model=dict[str, Any], summary="Get persisted settings")
|
|
15
15
|
def get_settings() -> JsonObject:
|
|
16
16
|
try:
|
|
17
17
|
return settings_service.get_persisted_settings_view()
|
|
18
18
|
except Exception as err:
|
|
19
19
|
raise HTTPException(status_code=500, detail=f"Server error: {str(err)}")
|
|
20
20
|
|
|
21
|
-
@router.get("/settings/effective", response_model=
|
|
21
|
+
@router.get("/settings/effective", response_model=dict[str, Any], summary="Get effective settings")
|
|
22
22
|
def get_effective_settings() -> JsonObject:
|
|
23
23
|
try:
|
|
24
24
|
return settings_service.get_effective_settings_view()
|
|
25
25
|
except Exception as err:
|
|
26
26
|
raise HTTPException(status_code=500, detail=f"Server error: {str(err)}")
|
|
27
27
|
|
|
28
|
-
@router.patch("/settings", response_model=
|
|
28
|
+
@router.patch("/settings", response_model=dict[str, Any], summary="Patch persisted settings")
|
|
29
29
|
def patch_settings(patch: SettingsPatchInput) -> JsonObject:
|
|
30
30
|
try:
|
|
31
31
|
return settings_service.patch_persisted_settings(cast(JsonObject, patch.root))
|
|
@@ -34,7 +34,7 @@ def build_settings_router(settings_service: SettingsService) -> APIRouter:
|
|
|
34
34
|
except Exception as err:
|
|
35
35
|
raise HTTPException(status_code=500, detail=f"Server error: {str(err)}")
|
|
36
36
|
|
|
37
|
-
@router.post("/settings/reset", response_model=
|
|
37
|
+
@router.post("/settings/reset", response_model=dict[str, Any], summary="Reset a persisted setting")
|
|
38
38
|
def reset_settings(payload: SettingsResetInput) -> JsonObject:
|
|
39
39
|
try:
|
|
40
40
|
return settings_service.reset_persisted_value(payload.path)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
1
|
from pydantic import BaseModel
|
|
4
2
|
|
|
5
3
|
from jukebox.shared.dependency_messages import optional_extra_dependency_message
|
|
@@ -62,7 +60,7 @@ class SelectedSonosGroupOutput(SelectedSonosGroupSettings):
|
|
|
62
60
|
class SonosSelectionMemberAvailabilityOutput(BaseModel):
|
|
63
61
|
uid: str
|
|
64
62
|
status: str
|
|
65
|
-
speaker:
|
|
63
|
+
speaker: SonosSpeakerOutput | None = None
|
|
66
64
|
|
|
67
65
|
|
|
68
66
|
class SonosSelectionAvailabilityOutput(BaseModel):
|
|
@@ -71,13 +69,13 @@ class SonosSelectionAvailabilityOutput(BaseModel):
|
|
|
71
69
|
|
|
72
70
|
|
|
73
71
|
class SonosSelectionOutput(BaseModel):
|
|
74
|
-
selected_group:
|
|
72
|
+
selected_group: SelectedSonosGroupOutput | None = None
|
|
75
73
|
availability: SonosSelectionAvailabilityOutput
|
|
76
74
|
|
|
77
75
|
|
|
78
76
|
class SonosSelectionInput(BaseModel):
|
|
79
77
|
uids: list[str]
|
|
80
|
-
coordinator_uid:
|
|
78
|
+
coordinator_uid: str | None = None
|
|
81
79
|
|
|
82
80
|
|
|
83
81
|
class SonosSelectionUpdateOutput(BaseModel):
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Union
|
|
3
2
|
|
|
4
3
|
from discstore.adapters.inbound.cli_display import display_library_line, display_library_table
|
|
5
4
|
from discstore.commands import (
|
|
@@ -43,24 +42,23 @@ class CLIController:
|
|
|
43
42
|
|
|
44
43
|
def run(
|
|
45
44
|
self,
|
|
46
|
-
command:
|
|
47
|
-
CliAddCommand, CliListCommand, CliRemoveCommand, CliEditCommand, CliGetCommand, CliSearchCommand
|
|
48
|
-
],
|
|
45
|
+
command: CliAddCommand | CliListCommand | CliRemoveCommand | CliEditCommand | CliGetCommand | CliSearchCommand,
|
|
49
46
|
) -> None:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
match command:
|
|
48
|
+
case CliAddCommand():
|
|
49
|
+
self.add_disc_flow(command)
|
|
50
|
+
case CliListCommand():
|
|
51
|
+
self.list_discs_flow(command)
|
|
52
|
+
case CliRemoveCommand():
|
|
53
|
+
self.remove_disc_flow(command)
|
|
54
|
+
case CliEditCommand():
|
|
55
|
+
self.edit_disc_flow(command)
|
|
56
|
+
case CliGetCommand():
|
|
57
|
+
self.get_disc_flow(command)
|
|
58
|
+
case CliSearchCommand():
|
|
59
|
+
self.search_discs_flow(command)
|
|
60
|
+
case _:
|
|
61
|
+
LOGGER.error("Command not implemented yet: command='%s'", command)
|
|
64
62
|
|
|
65
63
|
def add_disc_flow(self, command: CliAddCommand) -> None:
|
|
66
64
|
tag = self.resolve_tag_id.execute(command.tag, command.use_current_tag)
|
|
@@ -79,7 +77,7 @@ class CLIController:
|
|
|
79
77
|
if command.mode == "line":
|
|
80
78
|
display_library_line(discs)
|
|
81
79
|
return
|
|
82
|
-
LOGGER.error(
|
|
80
|
+
LOGGER.error("Displaying mode not implemented yet: mode='%s'", command.mode)
|
|
83
81
|
|
|
84
82
|
def remove_disc_flow(self, command: CliRemoveCommand) -> None:
|
|
85
83
|
tag = self.resolve_tag_id.execute(command.tag, command.use_current_tag)
|
|
@@ -122,7 +120,7 @@ class CLIController:
|
|
|
122
120
|
def search_discs_flow(self, command: CliSearchCommand) -> None:
|
|
123
121
|
results = self.search_discs.execute(command.query)
|
|
124
122
|
if not results:
|
|
125
|
-
LOGGER.info(
|
|
123
|
+
LOGGER.info("No discs found matching '%s'", command.query)
|
|
126
124
|
return
|
|
127
|
-
LOGGER.info(
|
|
125
|
+
LOGGER.info("Found %d disc(s) matching '%s':", len(results), command.query)
|
|
128
126
|
display_library_table(results)
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
from typing import Dict
|
|
2
|
-
|
|
3
1
|
from discstore.domain.entities import Disc
|
|
4
2
|
|
|
5
3
|
MAX_COL_WIDTH = 20
|
|
6
4
|
|
|
7
5
|
|
|
8
|
-
def display_library_line(discs:
|
|
6
|
+
def display_library_line(discs: dict[str, Disc]) -> None:
|
|
9
7
|
if not discs:
|
|
10
8
|
print("The library is empty")
|
|
11
9
|
return
|
|
@@ -28,7 +26,7 @@ def truncate(text: str, max_length: int) -> str:
|
|
|
28
26
|
return text[: max_length - 3] + "..."
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
def display_library_table(discs:
|
|
29
|
+
def display_library_table(discs: dict[str, Disc]) -> None:
|
|
32
30
|
if not discs:
|
|
33
31
|
print("The library is empty")
|
|
34
32
|
return
|
{gukebox-1.0.0.dev12 → gukebox-1.0.0.dev14}/discstore/adapters/inbound/interactive_cli_controller.py
RENAMED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
from discstore.adapters.inbound.cli_display import display_library_line, display_library_table
|
|
5
4
|
from discstore.domain.entities import CurrentTagStatus, Disc, DiscMetadata, DiscOption
|
|
@@ -38,24 +37,25 @@ class InteractiveCLIController:
|
|
|
38
37
|
|
|
39
38
|
def handle_command(self, command: str) -> None:
|
|
40
39
|
try:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
40
|
+
match command:
|
|
41
|
+
case "add":
|
|
42
|
+
self.add_disc_flow()
|
|
43
|
+
case "remove":
|
|
44
|
+
self.remove_disc_flow()
|
|
45
|
+
case "list":
|
|
46
|
+
self.list_discs_flow()
|
|
47
|
+
case "edit":
|
|
48
|
+
self.edit_disc_flow()
|
|
49
|
+
case "current":
|
|
50
|
+
self.current_tag_flow()
|
|
51
|
+
case "exit":
|
|
52
|
+
print("See you soon!")
|
|
53
|
+
exit(0)
|
|
54
|
+
case "help":
|
|
55
|
+
print(self.help_message)
|
|
56
|
+
case _:
|
|
57
|
+
print(f"Invalid command `{command}`")
|
|
58
|
+
print(self.help_message)
|
|
59
59
|
except Exception as err:
|
|
60
60
|
print(f"Error: {err}")
|
|
61
61
|
LOGGER.error("Error during handling command: %s", err)
|
|
@@ -112,7 +112,7 @@ class InteractiveCLIController:
|
|
|
112
112
|
print(f"Tag ID : {current_tag_status.tag_id}")
|
|
113
113
|
print(f"Known in library : {'yes' if current_tag_status.known_in_library else 'no'}")
|
|
114
114
|
|
|
115
|
-
def _prompt_for_tag(self, current_tag_status:
|
|
115
|
+
def _prompt_for_tag(self, current_tag_status: CurrentTagStatus | None, action: str) -> str:
|
|
116
116
|
default_tag = ""
|
|
117
117
|
if current_tag_status is not None and (
|
|
118
118
|
(action == "add" and not current_tag_status.known_in_library)
|