gukebox 1.4.0.dev1__tar.gz → 1.4.2__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.4.0.dev1 → gukebox-1.4.2}/PKG-INFO +4 -4
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/api/current_tag_router.py +4 -4
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/api/discs_router.py +4 -4
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/api/settings_router.py +4 -4
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/api_controller.py +3 -3
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/ui_controller.py +14 -9
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/ui_pages/settings.py +1 -1
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/players/sonos_player_adapter.py +1 -1
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/readers/pn532_reader_adapter.py +1 -1
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/sonos_discovery_adapter.py +17 -9
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/app.py +1 -1
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/cli_presentation.py +2 -2
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/ports/player_port.py +0 -4
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/ports/reader_port.py +0 -1
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/repositories/__init__.py +1 -1
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/sync_current_tag.py +1 -1
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/pn532/profiles.py +1 -1
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/shared/terminal_ui.py +1 -1
- gukebox-1.4.2/pyproject.toml +102 -0
- gukebox-1.4.0.dev1/pyproject.toml → gukebox-1.4.2/pyproject.toml.orig +22 -8
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/LICENSE +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/README.md +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/api/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/api/models.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/cli_controller.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/cli_display.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/interactive_cli_controller.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/ui_pages/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/ui_pages/library.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/ui_pages/sonos.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/cli_controller.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/json_library_adapter.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/players/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/players/dryrun_player_adapter.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/readers/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/readers/dryrun_reader_adapter.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/text_current_tag_adapter.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/command_handlers.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/commands.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/di_container.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/library_command_handlers.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/library_commands.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/pn532_command_handlers.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/pn532_commands.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/services.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/admin/sonos_households.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/app.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/di_container.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/entities/__init__.py +10 -10
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/entities/current_tag_state.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/entities/current_tag_status.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/entities/disc.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/entities/library.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/entities/playback_state.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/entities/tag_event.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/errors.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/ports/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/repositories/current_tag_repository.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/repositories/library_repository.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/__init__.py +1 -1
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/handle_tag_event.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/add_disc.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/edit_disc.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/get_current_tag_status.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/get_disc.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/list_discs.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/remove_disc.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/resolve_tag_id.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/search_discs.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/transition_current_tag.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/transition_playback.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/pn532/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/definitions.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/dict_utils.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/entities.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/errors.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/file_settings_repository.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/migration.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/repositories.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/resolve.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/runtime_resolver.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/runtime_validation.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/selected_sonos_group_repository.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/service_protocols.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/timing_validation.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/types.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/validation_rules.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/settings/view_utils.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/shared/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/shared/config_utils.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/shared/errors.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/shared/logger.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/shared/timing.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/sonos/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/sonos/discovery.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/sonos/selection.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/sonos/service.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/pn532/__init__.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/pn532/pn532.py +0 -0
- {gukebox-1.4.0.dev1 → gukebox-1.4.2}/pn532/spi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: gukebox
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.2
|
|
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
|
|
@@ -35,9 +35,9 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
35
35
|
Requires-Dist: pydantic==2.13.4
|
|
36
36
|
Requires-Dist: questionary==2.1.1
|
|
37
37
|
Requires-Dist: soco==0.31.1
|
|
38
|
-
Requires-Dist: typer==0.
|
|
39
|
-
Requires-Dist: fastapi==0.
|
|
40
|
-
Requires-Dist: uvicorn==0.
|
|
38
|
+
Requires-Dist: typer==0.27.0
|
|
39
|
+
Requires-Dist: fastapi==0.140.13 ; extra == 'api'
|
|
40
|
+
Requires-Dist: uvicorn==0.51.0 ; extra == 'api'
|
|
41
41
|
Requires-Dist: pyserial==3.5 ; extra == 'pn532'
|
|
42
42
|
Requires-Dist: spidev==3.8 ; extra == 'pn532'
|
|
43
43
|
Requires-Dist: lgpio==0.2.2.0 ; python_full_version < '3.13' and extra == 'pn532'
|
{gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/api/current_tag_router.py
RENAMED
|
@@ -36,7 +36,7 @@ def build_current_tag_router(
|
|
|
36
36
|
if actual_tag_id != expected_tag_id:
|
|
37
37
|
raise HTTPException(
|
|
38
38
|
status_code=409,
|
|
39
|
-
detail=f"Current tag changed: expected_tag_id='{expected_tag_id}', actual_tag_id={
|
|
39
|
+
detail=f"Current tag changed: expected_tag_id='{expected_tag_id}', actual_tag_id={actual_tag_id!r}",
|
|
40
40
|
)
|
|
41
41
|
|
|
42
42
|
def build_current_tag_disc_output(tag_id: str, disc: Disc) -> CurrentTagDiscOutput:
|
|
@@ -94,7 +94,7 @@ def build_current_tag_router(
|
|
|
94
94
|
except ValueError as value_err:
|
|
95
95
|
raise HTTPException(status_code=409, detail=str(value_err))
|
|
96
96
|
except Exception as err:
|
|
97
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
97
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
98
98
|
|
|
99
99
|
@router.patch(
|
|
100
100
|
"/current-tag/disc",
|
|
@@ -131,7 +131,7 @@ def build_current_tag_router(
|
|
|
131
131
|
except ValueError as value_err:
|
|
132
132
|
raise HTTPException(status_code=404, detail=str(value_err))
|
|
133
133
|
except Exception as err:
|
|
134
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
134
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
135
135
|
|
|
136
136
|
@router.delete(
|
|
137
137
|
"/current-tag/disc",
|
|
@@ -155,6 +155,6 @@ def build_current_tag_router(
|
|
|
155
155
|
except ValueError as value_err:
|
|
156
156
|
raise HTTPException(status_code=404, detail=str(value_err))
|
|
157
157
|
except Exception as err:
|
|
158
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
158
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
159
159
|
|
|
160
160
|
return router
|
|
@@ -26,7 +26,7 @@ def build_discs_router(
|
|
|
26
26
|
except ValueError as value_err:
|
|
27
27
|
raise HTTPException(status_code=404, detail=str(value_err))
|
|
28
28
|
except Exception as err:
|
|
29
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
29
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
30
30
|
|
|
31
31
|
@router.post("/discs/{tag_id}", response_model=DiscOutput, status_code=201, summary="Create a disc")
|
|
32
32
|
def create_disc_route(tag_id: str, disc: DiscInput) -> Disc:
|
|
@@ -36,7 +36,7 @@ def build_discs_router(
|
|
|
36
36
|
except ValueError as value_err:
|
|
37
37
|
raise HTTPException(status_code=409, detail=str(value_err))
|
|
38
38
|
except Exception as err:
|
|
39
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
39
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
40
40
|
|
|
41
41
|
@router.patch("/discs/{tag_id}", response_model=DiscOutput, summary="Update a disc")
|
|
42
42
|
def update_disc_route(tag_id: str, disc_patch: DiscPatchInput) -> Disc:
|
|
@@ -55,7 +55,7 @@ def build_discs_router(
|
|
|
55
55
|
except ValueError as value_err:
|
|
56
56
|
raise HTTPException(status_code=404, detail=str(value_err))
|
|
57
57
|
except Exception as err:
|
|
58
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
58
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
59
59
|
|
|
60
60
|
@router.delete("/discs/{tag_id}", status_code=204, summary="Delete a disc")
|
|
61
61
|
def remove_disc_route(tag_id: str) -> Response:
|
|
@@ -65,6 +65,6 @@ def build_discs_router(
|
|
|
65
65
|
except ValueError as value_err:
|
|
66
66
|
raise HTTPException(status_code=404, detail=str(value_err))
|
|
67
67
|
except Exception as err:
|
|
68
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
68
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
69
69
|
|
|
70
70
|
return router
|
|
@@ -16,14 +16,14 @@ def build_settings_router(settings_service: SettingsService) -> APIRouter:
|
|
|
16
16
|
try:
|
|
17
17
|
return settings_service.get_persisted_settings_view()
|
|
18
18
|
except Exception as err:
|
|
19
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
19
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
20
20
|
|
|
21
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
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
26
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
27
27
|
|
|
28
28
|
@router.patch("/settings", response_model=dict[str, Any], summary="Patch persisted settings")
|
|
29
29
|
def patch_settings(patch: SettingsPatchInput) -> JsonObject:
|
|
@@ -32,7 +32,7 @@ def build_settings_router(settings_service: SettingsService) -> APIRouter:
|
|
|
32
32
|
except SettingsError as err:
|
|
33
33
|
raise HTTPException(status_code=400, detail=str(err))
|
|
34
34
|
except Exception as err:
|
|
35
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
35
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
36
36
|
|
|
37
37
|
@router.post("/settings/reset", response_model=dict[str, Any], summary="Reset a persisted setting")
|
|
38
38
|
def reset_settings(payload: SettingsResetInput) -> JsonObject:
|
|
@@ -41,6 +41,6 @@ def build_settings_router(settings_service: SettingsService) -> APIRouter:
|
|
|
41
41
|
except SettingsError as err:
|
|
42
42
|
raise HTTPException(status_code=400, detail=str(err))
|
|
43
43
|
except Exception as err:
|
|
44
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
44
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
45
45
|
|
|
46
46
|
return router
|
|
@@ -134,7 +134,7 @@ class APIController:
|
|
|
134
134
|
except SonosDiscoveryError as err:
|
|
135
135
|
raise HTTPException(status_code=502, detail=str(err))
|
|
136
136
|
except Exception as err:
|
|
137
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
137
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
138
138
|
|
|
139
139
|
@self.app.get("/api/v1/sonos/selection", response_model=SonosSelectionOutput)
|
|
140
140
|
def get_sonos_selection():
|
|
@@ -146,7 +146,7 @@ class APIController:
|
|
|
146
146
|
except SonosDiscoveryError as err:
|
|
147
147
|
raise HTTPException(status_code=502, detail=str(err))
|
|
148
148
|
except Exception as err:
|
|
149
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
149
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
150
150
|
|
|
151
151
|
@self.app.put("/api/v1/sonos/selection", response_model=SonosSelectionUpdateOutput)
|
|
152
152
|
def put_sonos_selection(payload: SonosSelectionInput):
|
|
@@ -178,4 +178,4 @@ class APIController:
|
|
|
178
178
|
except HTTPException:
|
|
179
179
|
raise
|
|
180
180
|
except Exception as err:
|
|
181
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
181
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from typing import Annotated
|
|
2
3
|
|
|
3
4
|
from jukebox.shared.errors import MissingOptionalDependencyError
|
|
@@ -12,7 +13,7 @@ try:
|
|
|
12
13
|
except ModuleNotFoundError as e:
|
|
13
14
|
raise MissingOptionalDependencyError("The `ui_controller` module", "ui", "jukebox-admin ui") from e
|
|
14
15
|
|
|
15
|
-
from pydantic import BaseModel, Field
|
|
16
|
+
from pydantic import BaseModel, Field, ValidationError
|
|
16
17
|
|
|
17
18
|
from jukebox.adapters.inbound.admin.api_controller import APIController
|
|
18
19
|
from jukebox.adapters.inbound.admin.ui_pages.library import DiscForm, LibraryUIPageBuilder
|
|
@@ -28,6 +29,8 @@ from jukebox.sonos.discovery import SonosDiscoveryError
|
|
|
28
29
|
from jukebox.sonos.selection import SaveSonosSelection
|
|
29
30
|
from jukebox.sonos.service import SonosService
|
|
30
31
|
|
|
32
|
+
LOGGER = logging.getLogger("jukebox")
|
|
33
|
+
|
|
31
34
|
|
|
32
35
|
class SettingValueForm(BaseModel):
|
|
33
36
|
value: str = Field(title="Value")
|
|
@@ -109,7 +112,7 @@ class UIController(APIController):
|
|
|
109
112
|
except HTTPException:
|
|
110
113
|
raise
|
|
111
114
|
except Exception as err:
|
|
112
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
115
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
113
116
|
|
|
114
117
|
return self._build_success_response("toast-add-disc-success")
|
|
115
118
|
|
|
@@ -152,7 +155,7 @@ class UIController(APIController):
|
|
|
152
155
|
except HTTPException:
|
|
153
156
|
raise
|
|
154
157
|
except Exception as err:
|
|
155
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
158
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
156
159
|
|
|
157
160
|
return self._build_success_response("toast-edit-disc-success")
|
|
158
161
|
|
|
@@ -172,7 +175,7 @@ class UIController(APIController):
|
|
|
172
175
|
except ValueError as err:
|
|
173
176
|
raise HTTPException(status_code=404, detail=str(err))
|
|
174
177
|
except Exception as err:
|
|
175
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
178
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
176
179
|
|
|
177
180
|
return self._build_success_response("toast-remove-disc-success")
|
|
178
181
|
|
|
@@ -209,7 +212,7 @@ class UIController(APIController):
|
|
|
209
212
|
except HTTPException:
|
|
210
213
|
raise
|
|
211
214
|
except Exception as err:
|
|
212
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
215
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
213
216
|
|
|
214
217
|
return self.settings_pages.build_settings_success_response(str(result["message"]))
|
|
215
218
|
|
|
@@ -268,7 +271,7 @@ class UIController(APIController):
|
|
|
268
271
|
except HTTPException:
|
|
269
272
|
raise
|
|
270
273
|
except Exception as err:
|
|
271
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
274
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
272
275
|
|
|
273
276
|
return self.sonos_pages.build_sonos_success_response(str(result.settings_message))
|
|
274
277
|
|
|
@@ -299,14 +302,15 @@ class UIController(APIController):
|
|
|
299
302
|
except HTTPException:
|
|
300
303
|
raise
|
|
301
304
|
except Exception as err:
|
|
302
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
305
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
303
306
|
|
|
304
307
|
return self.sonos_pages.build_sonos_success_response(str(result.get("message", "Settings saved.")))
|
|
305
308
|
|
|
306
309
|
def _persisted_sonos_selection_matches(self, uids: list[str], coordinator_uid: str | None) -> bool:
|
|
307
310
|
try:
|
|
308
311
|
selected_group = SettingsSelectedSonosGroupRepository(self.settings_service).get_selected_group()
|
|
309
|
-
except
|
|
312
|
+
except (OSError, ValueError, ValidationError) as err:
|
|
313
|
+
LOGGER.warning("Failed to read persisted Sonos selection: %s", err)
|
|
310
314
|
return False
|
|
311
315
|
|
|
312
316
|
if selected_group is None:
|
|
@@ -325,7 +329,8 @@ class UIController(APIController):
|
|
|
325
329
|
|
|
326
330
|
try:
|
|
327
331
|
speakers = self.sonos_service.list_network_speakers()
|
|
328
|
-
except
|
|
332
|
+
except SonosDiscoveryError as err:
|
|
333
|
+
LOGGER.warning("Failed to list network speakers while building Sonos error message: %s", err)
|
|
329
334
|
return message
|
|
330
335
|
|
|
331
336
|
speaker = next((speaker for speaker in speakers if speaker.uid == coordinator_uid), None)
|
|
@@ -53,7 +53,7 @@ class SettingsUIPageBuilder:
|
|
|
53
53
|
except HTTPException:
|
|
54
54
|
raise
|
|
55
55
|
except Exception as err:
|
|
56
|
-
raise HTTPException(status_code=500, detail=f"Server error: {
|
|
56
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
57
57
|
|
|
58
58
|
return self.build_settings_success_response(str(result["message"]))
|
|
59
59
|
|
{gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/players/sonos_player_adapter.py
RENAMED
|
@@ -159,7 +159,7 @@ class SonosPlayerAdapter(PlayerPort):
|
|
|
159
159
|
speaker.player_name,
|
|
160
160
|
)
|
|
161
161
|
speaker.join(coordinator)
|
|
162
|
-
except
|
|
162
|
+
except (HTTPError, OSError, RequestException, RuntimeError, SoCoException, SoCoUPnPException) as err:
|
|
163
163
|
LOGGER.warning(
|
|
164
164
|
"Failed to roll back Sonos group change `%s` for `%s`: %s",
|
|
165
165
|
operation,
|
{gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/readers/pn532_reader_adapter.py
RENAMED
|
@@ -42,7 +42,7 @@ class Pn532ReaderAdapter(ReaderPort):
|
|
|
42
42
|
|
|
43
43
|
self.pn532 = PN532_SPI(debug=False, reset=spi_reset, cs=spi_cs, irq=spi_irq)
|
|
44
44
|
self.read_timeout_seconds = read_timeout_seconds
|
|
45
|
-
|
|
45
|
+
_ic, ver, rev, _support = self.pn532.get_firmware_version()
|
|
46
46
|
LOGGER.info("Found PN532 with firmware version: %s.%s", ver, rev)
|
|
47
47
|
self._firmware_version: tuple[int, int] = (ver, rev)
|
|
48
48
|
self.pn532.SAM_configuration()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ipaddress
|
|
2
|
+
import logging
|
|
2
3
|
import re
|
|
3
4
|
import select
|
|
4
5
|
import socket
|
|
@@ -7,6 +8,10 @@ import time
|
|
|
7
8
|
from dataclasses import dataclass
|
|
8
9
|
from typing import Any, Protocol
|
|
9
10
|
|
|
11
|
+
from requests.exceptions import RequestException
|
|
12
|
+
from soco.exceptions import SoCoException, SoCoUPnPException
|
|
13
|
+
from urllib3.exceptions import HTTPError
|
|
14
|
+
|
|
10
15
|
from jukebox.sonos.discovery import (
|
|
11
16
|
DiscoveredSonosSpeaker,
|
|
12
17
|
SonosDiscoveryError,
|
|
@@ -14,6 +19,9 @@ from jukebox.sonos.discovery import (
|
|
|
14
19
|
sort_sonos_speakers,
|
|
15
20
|
)
|
|
16
21
|
|
|
22
|
+
LOGGER = logging.getLogger("jukebox")
|
|
23
|
+
_SONOS_TRANSPORT_ERRORS = (HTTPError, OSError, RequestException, RuntimeError, SoCoException, SoCoUPnPException)
|
|
24
|
+
|
|
17
25
|
|
|
18
26
|
@dataclass(frozen=True)
|
|
19
27
|
class _SonosDiscoverySnapshot:
|
|
@@ -217,7 +225,7 @@ class SoCoSonosDiscoveryAdapter(SonosDiscoveryPort):
|
|
|
217
225
|
for speaker in list(discovered):
|
|
218
226
|
try:
|
|
219
227
|
available_speakers.update(speaker.all_zones)
|
|
220
|
-
except
|
|
228
|
+
except _SONOS_TRANSPORT_ERRORS:
|
|
221
229
|
available_speakers.add(speaker)
|
|
222
230
|
|
|
223
231
|
if not available_speakers:
|
|
@@ -333,8 +341,8 @@ def _safe_speaker_identifier(speaker: "_SonosSpeakerLike") -> str:
|
|
|
333
341
|
return ip_address
|
|
334
342
|
|
|
335
343
|
try:
|
|
336
|
-
uid =
|
|
337
|
-
except
|
|
344
|
+
uid = speaker.uid
|
|
345
|
+
except _SONOS_TRANSPORT_ERRORS:
|
|
338
346
|
return "unknown speaker"
|
|
339
347
|
|
|
340
348
|
return str(uid)
|
|
@@ -343,7 +351,7 @@ def _safe_speaker_identifier(speaker: "_SonosSpeakerLike") -> str:
|
|
|
343
351
|
def _safe_speaker_host(speaker: "_SonosSpeakerLike") -> str | None:
|
|
344
352
|
try:
|
|
345
353
|
ip_address = getattr(speaker, "ip_address", None)
|
|
346
|
-
except
|
|
354
|
+
except _SONOS_TRANSPORT_ERRORS:
|
|
347
355
|
return None
|
|
348
356
|
|
|
349
357
|
if ip_address:
|
|
@@ -353,8 +361,8 @@ def _safe_speaker_host(speaker: "_SonosSpeakerLike") -> str | None:
|
|
|
353
361
|
|
|
354
362
|
def _safe_speaker_uid(speaker: "_SonosSpeakerLike") -> str | None:
|
|
355
363
|
try:
|
|
356
|
-
return str(
|
|
357
|
-
except
|
|
364
|
+
return str(speaker.uid)
|
|
365
|
+
except _SONOS_TRANSPORT_ERRORS:
|
|
358
366
|
return None
|
|
359
367
|
|
|
360
368
|
|
|
@@ -373,15 +381,15 @@ def _build_private_ipv4_networks_to_scan() -> list[str]:
|
|
|
373
381
|
for adapter_ip in adapter.ips:
|
|
374
382
|
try:
|
|
375
383
|
ipv4_address = ipaddress.IPv4Address(adapter_ip.ip)
|
|
376
|
-
except
|
|
384
|
+
except ValueError:
|
|
385
|
+
LOGGER.debug("Skipping non-IPv4 network adapter address: %r", adapter_ip.ip)
|
|
377
386
|
continue
|
|
378
387
|
|
|
379
388
|
if adapter_ip.network_prefix >= 32:
|
|
380
389
|
continue
|
|
381
390
|
|
|
382
391
|
network_prefix = adapter_ip.network_prefix
|
|
383
|
-
|
|
384
|
-
network_prefix = _MAX_SCAN_NETWORK_PREFIX
|
|
392
|
+
network_prefix = max(network_prefix, _MAX_SCAN_NETWORK_PREFIX)
|
|
385
393
|
|
|
386
394
|
ipv4_network = ipaddress.ip_network(f"{ipv4_address}/{network_prefix}", strict=False)
|
|
387
395
|
if not ipv4_network.is_private or ipv4_network.is_loopback or ipv4_network.is_link_local:
|
|
@@ -67,7 +67,7 @@ def _version_callback(value: bool) -> None:
|
|
|
67
67
|
def _get_state(ctx: typer.Context) -> AdminCliState:
|
|
68
68
|
state = ctx.obj
|
|
69
69
|
if not isinstance(state, AdminCliState):
|
|
70
|
-
raise
|
|
70
|
+
raise TypeError("Admin CLI state was not initialized")
|
|
71
71
|
return state
|
|
72
72
|
|
|
73
73
|
|
|
@@ -50,7 +50,7 @@ def render_settings_output(
|
|
|
50
50
|
def render_cli_error(err: BaseException, verbose: bool = False) -> str:
|
|
51
51
|
message = _render_cli_error_message(err)
|
|
52
52
|
if verbose and str(err) and str(err) != message:
|
|
53
|
-
return f"{message}\n\nDetails: {
|
|
53
|
+
return f"{message}\n\nDetails: {err!s}"
|
|
54
54
|
return message
|
|
55
55
|
|
|
56
56
|
|
|
@@ -376,7 +376,7 @@ def _render_cli_error_message(err: BaseException) -> str:
|
|
|
376
376
|
return f"Malformed settings file at '{filepath}'. Fix the JSON syntax and try again."
|
|
377
377
|
return "Malformed settings file. Fix the JSON syntax and try again."
|
|
378
378
|
case UnsupportedSettingsVersionError():
|
|
379
|
-
return f"Unsupported settings file version. {
|
|
379
|
+
return f"Unsupported settings file version. {err!s}"
|
|
380
380
|
case InvalidSettingsError():
|
|
381
381
|
return _render_invalid_settings_error(err)
|
|
382
382
|
case SettingsError():
|
|
@@ -7,19 +7,15 @@ class PlayerPort(ABC):
|
|
|
7
7
|
@abstractmethod
|
|
8
8
|
def play(self, uri: str, shuffle: bool = False) -> None:
|
|
9
9
|
"""Start playing a URI with optional shuffle."""
|
|
10
|
-
pass
|
|
11
10
|
|
|
12
11
|
@abstractmethod
|
|
13
12
|
def pause(self) -> None:
|
|
14
13
|
"""Pause playback."""
|
|
15
|
-
pass
|
|
16
14
|
|
|
17
15
|
@abstractmethod
|
|
18
16
|
def resume(self) -> None:
|
|
19
17
|
"""Resume playback."""
|
|
20
|
-
pass
|
|
21
18
|
|
|
22
19
|
@abstractmethod
|
|
23
20
|
def stop(self) -> None:
|
|
24
21
|
"""Stop playback."""
|
|
25
|
-
pass
|
|
@@ -25,7 +25,7 @@ class SyncCurrentTag:
|
|
|
25
25
|
elif command == "clear":
|
|
26
26
|
self.repository.clear()
|
|
27
27
|
next_state = success_state
|
|
28
|
-
except Exception as err:
|
|
28
|
+
except Exception as err: # ruff: ignore[BLE001]
|
|
29
29
|
LOGGER.warning(
|
|
30
30
|
"Failed to sync current tag state; continuing, tag state may be stale: tag_id=%r, error=%s",
|
|
31
31
|
tag_event.tag_id,
|
|
@@ -59,7 +59,7 @@ def resolve_connection_params(
|
|
|
59
59
|
)
|
|
60
60
|
defaults = profile.connections[protocol]
|
|
61
61
|
if not isinstance(overrides, type(defaults)):
|
|
62
|
-
raise
|
|
62
|
+
raise TypeError(
|
|
63
63
|
f"Expected overrides of type {type(defaults).__name__} for protocol '{protocol}', "
|
|
64
64
|
f"got {type(overrides).__name__}"
|
|
65
65
|
)
|
|
@@ -7,6 +7,6 @@ def table(headers, rows, indexed=False):
|
|
|
7
7
|
widths = [max(len(str(x)) for x in col) for col in cols]
|
|
8
8
|
|
|
9
9
|
def fmt(row):
|
|
10
|
-
return " ".join(f"{
|
|
10
|
+
return " ".join(f"{val!s:<{widths[i]}}" for i, val in enumerate(row))
|
|
11
11
|
|
|
12
12
|
return "\n".join([fmt(headers), *map(fmt, rows)])
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gukebox"
|
|
3
|
+
version = "1.4.2"
|
|
4
|
+
description = "A Jukebox to play music on speakers using 'CD' with NFC tag"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11,<3.14"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"pydantic==2.13.4",
|
|
9
|
+
"questionary==2.1.1",
|
|
10
|
+
"soco==0.31.1",
|
|
11
|
+
"typer==0.27.0",
|
|
12
|
+
]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
]
|
|
22
|
+
keywords = [
|
|
23
|
+
"jukebox",
|
|
24
|
+
"music",
|
|
25
|
+
"nfc",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[[project.authors]]
|
|
29
|
+
name = "Gudsfile"
|
|
30
|
+
|
|
31
|
+
[project.license]
|
|
32
|
+
file = "LICENSE"
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Repository = "https://github.com/Gudsfile/jukebox"
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
api = [
|
|
39
|
+
"fastapi==0.140.13",
|
|
40
|
+
"uvicorn==0.51.0",
|
|
41
|
+
]
|
|
42
|
+
ui = [
|
|
43
|
+
"gukebox[api]",
|
|
44
|
+
"fastui==0.9.0",
|
|
45
|
+
"python-multipart==0.0.32",
|
|
46
|
+
]
|
|
47
|
+
pn532 = [
|
|
48
|
+
"pyserial==3.5",
|
|
49
|
+
"spidev==3.8",
|
|
50
|
+
"lgpio==0.2.2.0; python_version < '3.13'",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[project.scripts]
|
|
54
|
+
gukebox = "jukebox.app:main"
|
|
55
|
+
jukebox = "jukebox.app:main"
|
|
56
|
+
jukebox-admin = "jukebox.admin.app:main"
|
|
57
|
+
|
|
58
|
+
[dependency-groups]
|
|
59
|
+
dev = [
|
|
60
|
+
"pytest==9.1.1",
|
|
61
|
+
"pytest-mock==3.15.1",
|
|
62
|
+
"ruff==0.16.0",
|
|
63
|
+
"ty==0.0.64",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[tool.ruff]
|
|
67
|
+
line-length = 120
|
|
68
|
+
indent-width = 4
|
|
69
|
+
target-version = "py311"
|
|
70
|
+
extend-exclude = ["pn532/"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint]
|
|
73
|
+
extend-select = [
|
|
74
|
+
"SIM",
|
|
75
|
+
"I",
|
|
76
|
+
"UP",
|
|
77
|
+
"G",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
[tool.ruff.lint.per-file-ignores]
|
|
81
|
+
"jukebox/adapters/inbound/admin/api/current_tag_router.py" = ["BLE001"]
|
|
82
|
+
"jukebox/adapters/inbound/admin/api/discs_router.py" = ["BLE001"]
|
|
83
|
+
"jukebox/adapters/inbound/admin/api/settings_router.py" = ["BLE001"]
|
|
84
|
+
"jukebox/adapters/inbound/admin/api_controller.py" = ["BLE001"]
|
|
85
|
+
"jukebox/adapters/inbound/admin/interactive_cli_controller.py" = ["BLE001"]
|
|
86
|
+
"jukebox/adapters/inbound/admin/ui_controller.py" = ["BLE001"]
|
|
87
|
+
"jukebox/adapters/inbound/admin/ui_pages/settings.py" = ["BLE001"]
|
|
88
|
+
"jukebox/admin/app.py" = ["BLE001"]
|
|
89
|
+
|
|
90
|
+
[tool.ty.src]
|
|
91
|
+
exclude = ["pn532/"]
|
|
92
|
+
|
|
93
|
+
[tool.uv.build-backend]
|
|
94
|
+
module-name = [
|
|
95
|
+
"jukebox",
|
|
96
|
+
"pn532",
|
|
97
|
+
]
|
|
98
|
+
module-root = ""
|
|
99
|
+
|
|
100
|
+
[build-system]
|
|
101
|
+
requires = ["uv_build>=0.8.7,<0.13.0"]
|
|
102
|
+
build-backend = "uv_build"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gukebox"
|
|
3
|
-
version = "1.4.
|
|
3
|
+
version = "1.4.2"
|
|
4
4
|
description = "A Jukebox to play music on speakers using 'CD' with NFC tag"
|
|
5
5
|
authors = [{ name = "Gudsfile" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -10,7 +10,7 @@ dependencies = [
|
|
|
10
10
|
"pydantic==2.13.4",
|
|
11
11
|
"questionary==2.1.1",
|
|
12
12
|
"soco==0.31.1",
|
|
13
|
-
"typer==0.
|
|
13
|
+
"typer==0.27.0",
|
|
14
14
|
]
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 4 - Beta",
|
|
@@ -28,8 +28,8 @@ Repository = "https://github.com/Gudsfile/jukebox"
|
|
|
28
28
|
|
|
29
29
|
[project.optional-dependencies]
|
|
30
30
|
api = [
|
|
31
|
-
"fastapi==0.
|
|
32
|
-
"uvicorn==0.
|
|
31
|
+
"fastapi==0.140.13",
|
|
32
|
+
"uvicorn==0.51.0",
|
|
33
33
|
]
|
|
34
34
|
ui = [
|
|
35
35
|
"gukebox[api]",
|
|
@@ -45,10 +45,10 @@ pn532 = [
|
|
|
45
45
|
|
|
46
46
|
[dependency-groups]
|
|
47
47
|
dev = [
|
|
48
|
-
"pytest==9.
|
|
48
|
+
"pytest==9.1.1",
|
|
49
49
|
"pytest-mock==3.15.1",
|
|
50
|
-
"ruff==0.
|
|
51
|
-
"ty==0.0.
|
|
50
|
+
"ruff==0.16.0",
|
|
51
|
+
"ty==0.0.64",
|
|
52
52
|
]
|
|
53
53
|
|
|
54
54
|
[tool.ruff]
|
|
@@ -65,11 +65,25 @@ extend-select = [
|
|
|
65
65
|
"G",
|
|
66
66
|
]
|
|
67
67
|
|
|
68
|
+
[tool.ruff.lint.per-file-ignores]
|
|
69
|
+
# These inbound adapters (HTTP routers, UI/API controllers, CLI entry points) are the
|
|
70
|
+
# boundary layer whose job is to convert any unexpected failure into a safe response
|
|
71
|
+
# (an HTTP 500, a CLI error message) after more specific exceptions are already handled.
|
|
72
|
+
# A blind `except Exception` there is the intended last-resort safety net, not a bug.
|
|
73
|
+
"jukebox/adapters/inbound/admin/api/current_tag_router.py" = ["BLE001"]
|
|
74
|
+
"jukebox/adapters/inbound/admin/api/discs_router.py" = ["BLE001"]
|
|
75
|
+
"jukebox/adapters/inbound/admin/api/settings_router.py" = ["BLE001"]
|
|
76
|
+
"jukebox/adapters/inbound/admin/api_controller.py" = ["BLE001"]
|
|
77
|
+
"jukebox/adapters/inbound/admin/interactive_cli_controller.py" = ["BLE001"]
|
|
78
|
+
"jukebox/adapters/inbound/admin/ui_controller.py" = ["BLE001"]
|
|
79
|
+
"jukebox/adapters/inbound/admin/ui_pages/settings.py" = ["BLE001"]
|
|
80
|
+
"jukebox/admin/app.py" = ["BLE001"]
|
|
81
|
+
|
|
68
82
|
[tool.ty.src]
|
|
69
83
|
exclude = ["pn532/"]
|
|
70
84
|
|
|
71
85
|
[build-system]
|
|
72
|
-
requires = ["uv_build>=0.8.7,<0.
|
|
86
|
+
requires = ["uv_build>=0.8.7,<0.13.0"]
|
|
73
87
|
build-backend = "uv_build"
|
|
74
88
|
|
|
75
89
|
[tool.uv.build-backend]
|
|
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
|
{gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/inbound/admin/interactive_cli_controller.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
|
{gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/players/dryrun_player_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
{gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/adapters/outbound/readers/dryrun_reader_adapter.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -25,25 +25,25 @@ from .tag_event import TagEvent
|
|
|
25
25
|
|
|
26
26
|
__all__ = [
|
|
27
27
|
"CURRENT_TAG_ABSENCE_GRACE_SECONDS",
|
|
28
|
+
"PLAYBACK_RETRY_DELAYS_SECONDS",
|
|
28
29
|
"CurrentTagCommand",
|
|
29
30
|
"CurrentTagContext",
|
|
30
31
|
"CurrentTagState",
|
|
31
32
|
"CurrentTagStatus",
|
|
33
|
+
"Disc",
|
|
34
|
+
"DiscMetadata",
|
|
35
|
+
"DiscOption",
|
|
32
36
|
"Idle",
|
|
37
|
+
"Library",
|
|
33
38
|
"NoTag",
|
|
34
|
-
"TagPresent",
|
|
35
|
-
"TagRemoved",
|
|
36
|
-
"Playing",
|
|
37
|
-
"Waiting",
|
|
38
39
|
"Paused",
|
|
39
|
-
"PLAYBACK_RETRY_DELAYS_SECONDS",
|
|
40
40
|
"PlaybackCommand",
|
|
41
41
|
"PlaybackState",
|
|
42
|
+
"Playing",
|
|
42
43
|
"RetryState",
|
|
43
|
-
"TransitionContext",
|
|
44
44
|
"TagEvent",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
45
|
+
"TagPresent",
|
|
46
|
+
"TagRemoved",
|
|
47
|
+
"TransitionContext",
|
|
48
|
+
"Waiting",
|
|
49
49
|
]
|
|
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
|
|
@@ -10,11 +10,11 @@ from .library.search_discs import SearchDiscs
|
|
|
10
10
|
from .sync_current_tag import SyncCurrentTag
|
|
11
11
|
|
|
12
12
|
__all__ = [
|
|
13
|
-
"HandleTagEvent",
|
|
14
13
|
"AddDisc",
|
|
15
14
|
"EditDisc",
|
|
16
15
|
"GetCurrentTagStatus",
|
|
17
16
|
"GetDisc",
|
|
17
|
+
"HandleTagEvent",
|
|
18
18
|
"ListDiscs",
|
|
19
19
|
"RemoveDisc",
|
|
20
20
|
"ResolveTagId",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gukebox-1.4.0.dev1 → gukebox-1.4.2}/jukebox/domain/use_cases/library/get_current_tag_status.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
|
|
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
|
|
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
|