gukebox 1.4.2__tar.gz → 1.5.0.dev1__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.2 → gukebox-1.5.0.dev1}/PKG-INFO +6 -6
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/api/current_tag_router.py +41 -1
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/api/discs_router.py +4 -1
- gukebox-1.5.0.dev1/jukebox/adapters/inbound/admin/api/models.py +86 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/api/settings_router.py +33 -1
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/api_controller.py +13 -0
- gukebox-1.5.0.dev1/jukebox/adapters/inbound/admin/static/assets/index-B2mMrbHV.js +3 -0
- gukebox-1.5.0.dev1/jukebox/adapters/inbound/admin/static/assets/index-BhofQAra.css +1 -0
- gukebox-1.5.0.dev1/jukebox/adapters/inbound/admin/static/favicon.svg +1 -0
- gukebox-1.5.0.dev1/jukebox/adapters/inbound/admin/static/index.html +14 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/sonos_discovery_adapter.py +3 -2
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/app.py +32 -11
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/command_handlers.py +1 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/commands.py +1 -29
- gukebox-1.5.0.dev1/jukebox/shared/logger.py +15 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/pyproject.toml +8 -8
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/pyproject.toml.orig +8 -8
- gukebox-1.4.2/jukebox/adapters/inbound/admin/api/models.py +0 -48
- gukebox-1.4.2/jukebox/shared/logger.py +0 -13
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/LICENSE +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/README.md +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/api/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/cli_controller.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/cli_display.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/interactive_cli_controller.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/ui_controller.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/ui_pages/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/ui_pages/library.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/ui_pages/settings.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/ui_pages/sonos.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/cli_controller.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/json_library_adapter.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/players/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/players/dryrun_player_adapter.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/players/sonos_player_adapter.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/readers/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/readers/dryrun_reader_adapter.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/readers/pn532_reader_adapter.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/text_current_tag_adapter.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/cli_presentation.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/di_container.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/library_command_handlers.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/library_commands.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/pn532_command_handlers.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/pn532_commands.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/services.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/admin/sonos_households.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/app.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/di_container.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/entities/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/entities/current_tag_state.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/entities/current_tag_status.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/entities/disc.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/entities/library.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/entities/playback_state.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/entities/tag_event.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/errors.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/ports/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/ports/player_port.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/ports/reader_port.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/repositories/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/repositories/current_tag_repository.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/repositories/library_repository.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/handle_tag_event.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/library/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/library/add_disc.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/library/edit_disc.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/library/get_current_tag_status.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/library/get_disc.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/library/list_discs.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/library/remove_disc.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/library/resolve_tag_id.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/library/search_discs.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/sync_current_tag.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/transition_current_tag.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/domain/use_cases/transition_playback.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/pn532/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/pn532/profiles.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/definitions.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/dict_utils.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/entities.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/errors.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/file_settings_repository.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/migration.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/repositories.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/resolve.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/runtime_resolver.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/runtime_validation.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/selected_sonos_group_repository.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/service_protocols.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/timing_validation.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/types.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/validation_rules.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/settings/view_utils.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/shared/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/shared/config_utils.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/shared/errors.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/shared/terminal_ui.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/shared/timing.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/sonos/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/sonos/discovery.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/sonos/selection.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/sonos/service.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/pn532/__init__.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/pn532/pn532.py +0 -0
- {gukebox-1.4.2 → gukebox-1.5.0.dev1}/pn532/spi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: gukebox
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0.dev1
|
|
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
|
|
@@ -32,12 +32,12 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
32
32
|
Classifier: Programming Language :: Python :: 3.11
|
|
33
33
|
Classifier: Programming Language :: Python :: 3.12
|
|
34
34
|
Classifier: Programming Language :: Python :: 3.13
|
|
35
|
-
Requires-Dist: pydantic==2.13.
|
|
35
|
+
Requires-Dist: pydantic==2.13.5
|
|
36
36
|
Requires-Dist: questionary==2.1.1
|
|
37
|
-
Requires-Dist: soco==0.31.
|
|
38
|
-
Requires-Dist: typer==0.27.
|
|
39
|
-
Requires-Dist: fastapi==0.
|
|
40
|
-
Requires-Dist: uvicorn==0.
|
|
37
|
+
Requires-Dist: soco==0.31.2
|
|
38
|
+
Requires-Dist: typer==0.27.2
|
|
39
|
+
Requires-Dist: fastapi==0.141.1 ; extra == 'api'
|
|
40
|
+
Requires-Dist: uvicorn==0.52.4 ; 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.2 → gukebox-1.5.0.dev1}/jukebox/adapters/inbound/admin/api/current_tag_router.py
RENAMED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections.abc import AsyncIterator
|
|
1
3
|
from typing import Any
|
|
2
4
|
|
|
3
|
-
from fastapi import APIRouter, HTTPException, Response, status
|
|
5
|
+
from fastapi import APIRouter, HTTPException, Request, Response, status
|
|
6
|
+
from fastapi.responses import StreamingResponse
|
|
4
7
|
from pydantic import ValidationError
|
|
5
8
|
|
|
6
9
|
from jukebox.adapters.inbound.admin.api.models import (
|
|
@@ -42,6 +45,28 @@ def build_current_tag_router(
|
|
|
42
45
|
def build_current_tag_disc_output(tag_id: str, disc: Disc) -> CurrentTagDiscOutput:
|
|
43
46
|
return CurrentTagDiscOutput(tag_id=tag_id, disc=DiscOutput(**disc.model_dump()))
|
|
44
47
|
|
|
48
|
+
def serialize_current_tag_status(current_tag_status: CurrentTagStatus | None) -> str:
|
|
49
|
+
if current_tag_status is None:
|
|
50
|
+
return "null"
|
|
51
|
+
return CurrentTagStatusOutput(**current_tag_status.model_dump()).model_dump_json()
|
|
52
|
+
|
|
53
|
+
async def current_tag_status_event_stream(
|
|
54
|
+
request: Request,
|
|
55
|
+
poll_interval_seconds: float = 0.5,
|
|
56
|
+
) -> AsyncIterator[bytes]:
|
|
57
|
+
previous_payload: str | None = None
|
|
58
|
+
|
|
59
|
+
while True:
|
|
60
|
+
payload = serialize_current_tag_status(read_current_tag_status())
|
|
61
|
+
if payload != previous_payload:
|
|
62
|
+
previous_payload = payload
|
|
63
|
+
yield f"data: {payload}\n\n".encode()
|
|
64
|
+
|
|
65
|
+
if await request.is_disconnected():
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
await asyncio.sleep(poll_interval_seconds)
|
|
69
|
+
|
|
45
70
|
@router.get(
|
|
46
71
|
"/current-tag",
|
|
47
72
|
response_model=CurrentTagStatusOutput,
|
|
@@ -55,6 +80,21 @@ def build_current_tag_router(
|
|
|
55
80
|
|
|
56
81
|
return CurrentTagStatusOutput(**current_tag_status.model_dump())
|
|
57
82
|
|
|
83
|
+
@router.get(
|
|
84
|
+
"/current-tag/events",
|
|
85
|
+
summary="Stream current NFC tag status changes as Server-Sent Events",
|
|
86
|
+
)
|
|
87
|
+
async def get_current_tag_events(request: Request) -> StreamingResponse:
|
|
88
|
+
return StreamingResponse(
|
|
89
|
+
current_tag_status_event_stream(request),
|
|
90
|
+
media_type="text/event-stream",
|
|
91
|
+
headers={
|
|
92
|
+
"Cache-Control": "no-cache",
|
|
93
|
+
"Connection": "keep-alive",
|
|
94
|
+
"X-Accel-Buffering": "no",
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
|
|
58
98
|
@router.get(
|
|
59
99
|
"/current-tag/disc",
|
|
60
100
|
response_model=CurrentTagDiscOutput,
|
|
@@ -17,7 +17,10 @@ def build_discs_router(
|
|
|
17
17
|
|
|
18
18
|
@router.get("/discs", response_model=dict[str, DiscOutput], summary="List discs")
|
|
19
19
|
def list_discs_route() -> dict[str, Disc]:
|
|
20
|
-
|
|
20
|
+
try:
|
|
21
|
+
return list_discs.execute()
|
|
22
|
+
except Exception as err:
|
|
23
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
21
24
|
|
|
22
25
|
@router.get("/discs/{tag_id}", response_model=DiscOutput, summary="Get a disc")
|
|
23
26
|
def get_disc_route(tag_id: str) -> Disc:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, RootModel, computed_field
|
|
4
|
+
|
|
5
|
+
from jukebox.domain.entities import CurrentTagStatus, Disc
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DiscInput(Disc):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DiscOutput(Disc):
|
|
13
|
+
@computed_field
|
|
14
|
+
@property
|
|
15
|
+
def display_type(self) -> str:
|
|
16
|
+
return self.metadata.display_type
|
|
17
|
+
|
|
18
|
+
@computed_field
|
|
19
|
+
@property
|
|
20
|
+
def display_title(self) -> str:
|
|
21
|
+
return self.metadata.display_title
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DiscPatchMetadataInput(BaseModel):
|
|
25
|
+
artist: str | None = None
|
|
26
|
+
album: str | None = None
|
|
27
|
+
track: str | None = None
|
|
28
|
+
playlist: str | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class DiscPatchOptionInput(BaseModel):
|
|
32
|
+
shuffle: bool | None = None
|
|
33
|
+
is_test: bool | None = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DiscPatchInput(BaseModel):
|
|
37
|
+
uri: str | None = None
|
|
38
|
+
metadata: DiscPatchMetadataInput | None = None
|
|
39
|
+
option: DiscPatchOptionInput | None = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class CurrentTagStatusOutput(CurrentTagStatus):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CurrentTagDiscOutput(BaseModel):
|
|
47
|
+
tag_id: str
|
|
48
|
+
disc: DiscOutput
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SettingsResetInput(BaseModel):
|
|
52
|
+
path: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SettingsPatchInput(RootModel[dict[str, Any]]):
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class SettingChoiceOutput(BaseModel):
|
|
60
|
+
value: str
|
|
61
|
+
label: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class EditableSettingDisplayOutput(BaseModel):
|
|
65
|
+
path: str
|
|
66
|
+
label: str
|
|
67
|
+
description: str
|
|
68
|
+
field_type: str
|
|
69
|
+
section: str
|
|
70
|
+
section_label: str
|
|
71
|
+
section_description: str
|
|
72
|
+
section_sort_order: int
|
|
73
|
+
requires_restart: bool
|
|
74
|
+
advanced: bool
|
|
75
|
+
choices: list[SettingChoiceOutput]
|
|
76
|
+
default_value: Any
|
|
77
|
+
persisted_value: Any
|
|
78
|
+
effective_value: Any
|
|
79
|
+
provenance: str
|
|
80
|
+
is_persisted: bool
|
|
81
|
+
is_pinned_default: bool
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class SettingsDisplaysOutput(BaseModel):
|
|
85
|
+
settings: list[EditableSettingDisplayOutput]
|
|
86
|
+
effective_settings_error: str | None = None
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import dataclasses
|
|
1
2
|
from typing import Any, cast
|
|
2
3
|
|
|
3
4
|
from fastapi import APIRouter, HTTPException
|
|
4
5
|
|
|
5
|
-
from jukebox.adapters.inbound.admin.api.models import
|
|
6
|
+
from jukebox.adapters.inbound.admin.api.models import (
|
|
7
|
+
SettingsDisplaysOutput,
|
|
8
|
+
SettingsPatchInput,
|
|
9
|
+
SettingsResetInput,
|
|
10
|
+
)
|
|
11
|
+
from jukebox.settings.definitions import build_editable_setting_displays
|
|
6
12
|
from jukebox.settings.errors import SettingsError
|
|
7
13
|
from jukebox.settings.service_protocols import SettingsService
|
|
8
14
|
from jukebox.settings.types import JsonObject
|
|
@@ -25,6 +31,32 @@ def build_settings_router(settings_service: SettingsService) -> APIRouter:
|
|
|
25
31
|
except Exception as err:
|
|
26
32
|
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
27
33
|
|
|
34
|
+
@router.get(
|
|
35
|
+
"/settings/displays",
|
|
36
|
+
response_model=SettingsDisplaysOutput,
|
|
37
|
+
summary="Get settings with display metadata (labels, choices, provenance)",
|
|
38
|
+
)
|
|
39
|
+
def get_settings_displays() -> SettingsDisplaysOutput:
|
|
40
|
+
try:
|
|
41
|
+
persisted_settings = settings_service.get_persisted_settings_view()
|
|
42
|
+
except Exception as err:
|
|
43
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
44
|
+
|
|
45
|
+
effective_settings_error: str | None = None
|
|
46
|
+
try:
|
|
47
|
+
effective_settings_view = settings_service.get_effective_settings_view()
|
|
48
|
+
except SettingsError as err:
|
|
49
|
+
effective_settings_view = {}
|
|
50
|
+
effective_settings_error = str(err)
|
|
51
|
+
except Exception as err:
|
|
52
|
+
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
53
|
+
|
|
54
|
+
displays = build_editable_setting_displays(persisted_settings, effective_settings_view)
|
|
55
|
+
return SettingsDisplaysOutput(
|
|
56
|
+
settings=[dataclasses.asdict(display) for display in displays],
|
|
57
|
+
effective_settings_error=effective_settings_error,
|
|
58
|
+
)
|
|
59
|
+
|
|
28
60
|
@router.patch("/settings", response_model=dict[str, Any], summary="Patch persisted settings")
|
|
29
61
|
def patch_settings(patch: SettingsPatchInput) -> JsonObject:
|
|
30
62
|
try:
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
|
|
1
3
|
from pydantic import BaseModel
|
|
2
4
|
|
|
3
5
|
from jukebox.shared.errors import MissingOptionalDependencyError
|
|
4
6
|
|
|
5
7
|
try:
|
|
6
8
|
from fastapi import FastAPI, HTTPException
|
|
9
|
+
from fastapi.responses import RedirectResponse
|
|
10
|
+
from fastapi.staticfiles import StaticFiles
|
|
7
11
|
|
|
8
12
|
from jukebox.adapters.inbound.admin.api.current_tag_router import build_current_tag_router
|
|
9
13
|
from jukebox.adapters.inbound.admin.api.discs_router import build_discs_router
|
|
@@ -78,6 +82,9 @@ class SonosSelectionUpdateOutput(BaseModel):
|
|
|
78
82
|
restart_required: bool
|
|
79
83
|
|
|
80
84
|
|
|
85
|
+
STATIC_DIR = pathlib.Path(__file__).parent / "static"
|
|
86
|
+
|
|
87
|
+
|
|
81
88
|
class APIController:
|
|
82
89
|
def __init__(
|
|
83
90
|
self,
|
|
@@ -179,3 +186,9 @@ class APIController:
|
|
|
179
186
|
raise
|
|
180
187
|
except Exception as err:
|
|
181
188
|
raise HTTPException(status_code=500, detail=f"Server error: {err!s}")
|
|
189
|
+
|
|
190
|
+
@self.app.get("/ui", include_in_schema=False)
|
|
191
|
+
def redirect_to_admin_ui():
|
|
192
|
+
return RedirectResponse(url="/ui/")
|
|
193
|
+
|
|
194
|
+
self.app.mount("/ui", StaticFiles(directory=STATIC_DIR, html=True), name="admin-ui")
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=Array.isArray,t=Array.prototype.indexOf,n=Array.prototype.includes,r=Array.from,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyDescriptors,s=Object.prototype,c=Array.prototype,l=Object.getPrototypeOf,u=Object.isExtensible,d=()=>{};function f(e){for(var t=0;t<e.length;t++)e[t]()}function p(){var e,t;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}function m(e,t){if(Array.isArray(e))return e;if(t===void 0||!(Symbol.iterator in e))return Array.from(e);let n=[];for(let r of e)if(n.push(r),n.length===t)break;return n}var h=1024,g=2048,_=4096,v=8192,y=16384,b=32768,x=1<<25,S=65536,C=1<<19,w=1<<20,T=1<<25,E=65536,ee=1<<21,te=1<<22,ne=1<<23,re=Symbol(`$state`),ie=Symbol(`component`),ae=Symbol(`legacy props`),oe=Symbol(``),se=Symbol(`attributes`),ce=Symbol(`class`),le=Symbol(`style`),ue=Symbol(`text`),de=Symbol(`form reset`),fe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},pe=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`),me={},D=Symbol(`uninitialized`),he=`http://www.w3.org/1999/xhtml`;function ge(){console.warn(`https://svelte.dev/e/derived_inert`)}function _e(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function ve(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function ye(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var O=!1;function be(e){O=e}var k;function xe(e){if(e===null)throw _e(),me;return k=e}function Se(){return xe(rn(k))}function A(e){if(O){if(rn(k)!==null)throw _e(),me;k=e}}function Ce(e=1){if(O){for(var t=e,n=k;t--;)n=rn(n);k=n}}function we(e=!0){for(var t=0,n=k;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=rn(n);e&&n.remove(),n=i}}function Te(e){if(!e||e.nodeType!==8)throw _e(),me;return e.data}function Ee(e){return e===this.v}function De(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Oe(e){return!De(e,this.v)}function ke(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ae(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function je(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Me(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function Ne(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Pe(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Fe(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function Ie(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Le(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function Re(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function ze(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Be(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var j=null;function Ve(e){j=e}function He(e,t=!1,n){j={p:j,i:!1,c:null,e:null,s:e,x:null,r:W,l:null}}function Ue(e){var t=j,n=t.e;if(n!==null){t.e=null;for(var r of n)vn(r)}return e!==void 0&&(t.x=e),t.i=!0,j=t.p,We(e)}function We(e={}){return i(e,ie,{value:!0}),e}function Ge(){return!0}var Ke=[];function qe(){var e=Ke;Ke=[],f(e)}function Je(e){if(Ke.length===0&&!Tt){var t=Ke;queueMicrotask(()=>{t===Ke&&qe()})}Ke.push(e)}function Ye(){for(;Ke.length>0;)qe()}var Xe=~(g|_|h);function M(e,t){e.f=e.f&Xe|t}function Ze(e){e.f&512||e.deps===null?M(e,h):M(e,_)}function Qe(e){if(e!==null)for(let t of e)!(t.f&2)||!(t.f&65536)||(t.f^=E,Qe(t.deps))}function $e(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),Qe(e.deps),M(e,h)}var et=!1;function tt(e){var t=et;try{return et=!1,[e(),et]}finally{et=t}}function nt(e){O&&nn(e)!==null&&on(e)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[de]?.()})},{capture:!0}))}function at(e){var t=U,n=W;zn(null),Bn(null);try{return e()}finally{zn(t),Bn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[de];e[de]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ge()?dt:ht;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=W,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){dn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>dn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=W,t=U,n=j,r=N;return function(i=!0){Bn(e),zn(t),Ve(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Bn(null),zn(null),Ve(null),e&&N?.deactivate()}function ut(){var e=W,t=e.b,n=N,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|g;return W!==null&&(W.f|=C),{ctx:j,deps:null,effects:null,equals:Ee,f:t,fn:e,reactions:null,rv:0,v:D,wv:0,parent:W,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let r=W;r===null&&Ae();var i=void 0,a=Ht(D),o=!U,s=new Set;return xn(()=>{var t=W,n=p();i=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==fe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var c=N;if(o){if(t.f&32768)var l=ut();if(r.b?.is_rendered())c.async_deriveds.get(t)?.reject(ft);else for(let e of s.values())e.reject(ft);s.add(n),c.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),s.delete(n),t!==ft&&(c.activate(),t?(a.f|=ne,Wt(a,t)):(a.f&8388608&&(a.f^=ne),Wt(a,e)),c.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),gn(()=>{for(let e of s)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===i?e(a):t(i)}n.then(r,r)}t(i)})}function mt(e){let t=dt(e);return Hn(t),t}function ht(e){let t=dt(e);return t.equals=Oe,t}function gt(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n<t.length;n+=1)H(t[n])}}function _t(e){var t,n=W,r=e.parent;if(!In&&r!==null&&e.v!==D&&r.f&24576)return ge(),e.v;Bn(r);try{e.f&=~E,gt(e),t=Qn(e)}finally{Bn(n)}return t}function vt(e){var t=_t(e);if(!e.equals(t)&&(e.wv=Yn(),(!N?.is_fork||e.deps===null)&&(N===null?e.v=t:(N.capture(e,t,!0),St?.capture(e,t,!0)),e.deps===null))){M(e,h);return}In||(Ct===null?Ze(e):(hn()||N?.is_fork)&&Ct.set(e,t))}function yt(e){if(e.effects!==null)for(let t of e.effects)(t.teardown||t.ac)&&(t.teardown?.(),t.ac!==null&&at(()=>{t.ac.abort(fe),t.ac=null}),t.fn!==null&&(t.teardown=d),tr(t,0),Tn(t))}function bt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&nr(t)}var xt=null,N=null,St=null,Ct=null,wt=null,Tt=!1,Et=!1,Dt=null,Ot=null,kt=0,At=1,jt=class e{id=At++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){xt===null?xt=this:(xt.#n=this,this.#t=xt),xt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)M(r,g),t(r);for(r of n.m)M(r,_),t(r)}this.#p.add(e)}#g(){this.#e=!0,kt++>1e3&&(this.#x(),Nt());for(let e of this.#u)this.#d.delete(e),M(e,g),this.schedule(e);for(let e of this.#d)M(e,_),this.schedule(e);let t=this.#c;this.#c=[],this.apply();var n=Dt=[],r=[],i=Ot=[];for(let e of t)try{this.#_(e,n,r)}catch(t){throw Rt(e),this.#h()||this.discard(),t}if(N=null,i.length>0){var a=e.ensure();for(let e of i)a.schedule(e)}if(Dt=null,Ot=null,this.#h()){this.#b(r),this.#b(n);for(let[e,t]of this.#f)Lt(e,t);i.length>0&&N.#g();return}let o=this.#v();if(o){this.#b(r),this.#b(n),o.#y(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),St=this,Ft(r),Ft(n),St=null,this.#s?.resolve();var s=N;if(this.#a===0&&(this.#c.length===0||s!==null)&&this.#x(),this.#c.length>0){if(s!==null){let e=s;e.#c.push(...this.#c.filter(t=>!e.#c.includes(t)))}else s=this}s!==null&&(Bt.clear(),s.#g())}#_(e,t,n){e.f^=h;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=h:i&4?t.push(r):Xn(r)&&(i&16&&this.#d.add(r),nr(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#v(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#y(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),M(i,g),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#x(),N=this,this.#g()}#b(e){for(var t=0;t<e.length;t+=1)$e(e[t],this.#u,this.#d)}capture(e,t,n=!1){e.v!==D&&!this.previous.has(e)&&this.previous.set(e,e.v),e.f&8388608||(this.current.set(e,[t,n]),Ct?.set(e,t)),this.is_fork||(e.v=t)}activate(){N=this}deactivate(){N=null,Ct=null}flush(){try{Et=!0,N=this,this.#g()}finally{kt=0,wt=null,Dt=null,Ot=null,Et=!1,N=null,Ct=null,Bt.clear()}}discard(){for(let e of this.#i)e(this);this.#i.clear();for(let e of this.async_deriveds.values())e.reject(ft);this.#x(),this.#s?.resolve()}register_created_effect(e){this.#l.push(e)}increment(e,t){if(this.#a+=1,e){let e=this.#o.get(t)??0;this.#o.set(t,e+1)}}decrement(e,t){if(--this.#a,e){let e=this.#o.get(t)??0;e===1?this.#o.delete(t):this.#o.set(t,e-1)}this.#m||(this.#m=!0,Je(()=>{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=p()).promise}static ensure(){if(N===null){let t=N=new e;!Et&&!Tt&&Je(()=>{t.#e||t.flush()})}return N}apply(){Ct=null}schedule(e){if(wt=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}for(var t=e;t.parent!==null;){t=t.parent;var n=t.f;if(Dt!==null&&t===W&&(U===null||!(U.f&2)))return;if(n&96){if(!(n&1024))return;t.f^=h}}this.#c.push(t)}#x(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?xt=e:t.#t=e,this.linked=!1}}};function Mt(e){var t=Tt;Tt=!0;try{var n;for(e&&(N!==null&&!N.is_fork&&N.flush(),n=e());;){if(Ye(),N===null)return n;N.flush()}}finally{Tt=t}}function Nt(){try{Fe()}catch(e){dn(e,wt)}}var Pt=null;function Ft(e){var t=e.length;if(t!==0){for(var n=0;n<t;){var r=e[n++];if(!(r.f&24576)&&Xn(r)&&(Pt=new Set,nr(r),r.deps===null&&r.first===null&&r.nodes===null&&r.teardown===null&&r.ac===null&&On(r),Pt?.size>0)){Bt.clear();for(let e of Pt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Pt.has(n)&&(Pt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||nr(n)}}Pt.clear()}}Pt=null}}function It(e){N.schedule(e)}function Lt(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),M(e,h);for(var n=e.first;n!==null;)Lt(n,t),n=n.next}}function Rt(e){M(e,h);for(var t=e.first;t!==null;)Rt(t),t=t.next}var zt=new Set,Bt=new Map,Vt=!1;function Ht(e,t){return{f:0,v:e,reactions:null,equals:Ee,rv:0,wv:0}}function P(e,t){let n=Ht(e,t);return Hn(n),n}function Ut(e,t=!1,n=!0){let r=Ht(e);return t||(r.equals=Oe),r}function F(e,t,n=!1){return U!==null&&(!Rn||U.f&131072)&&Ge()&&U.f&4325394&&(Vn===null||!Vn.has(e))&&ze(),Wt(e,n?I(t):t,Ot)}function Wt(e,t,n=null){if(!e.equals(t)){In?Bt.set(e,t):Bt.has(e)||Bt.set(e,e.v);var r=jt.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&&_t(t),Ct===null&&Ze(t)}e.wv=Yn(),qt(e,g,n),Ge()&&W!==null&&W.f&1024&&!(W.f&96)&&(Un===null?Wn([e]):Un.push(e)),!r.is_fork&&zt.size>0&&!Vt&&Gt()}return t}function Gt(){Vt=!1;for(let e of zt){e.f&1024&&M(e,_);let t;try{t=Xn(e)}catch{t=!0}t&&nr(e)}zt.clear()}function Kt(e){F(e,e.v+1)}function qt(e,t,n){var r=e.reactions;if(r!==null)for(var i=Ge(),a=r.length,o=0;o<a;o++){var s=r[o],c=s.f;if(!(!i&&s===W)){var l=(c&g)===0;if(l&&M(s,t),c&131072)zt.add(s);else if(c&2){var u=s;Ct?.delete(u),c&65536||(c&512&&(W===null||!(W.f&2097152))&&(s.f|=E),qt(u,_,n))}else if(l){var d=s;c&16&&Pt!==null&&Pt.add(d),n===null?It(d):n.push(d)}}}}function I(t){if(typeof t!=`object`||!t||re in t||ie in t)return t;let n=l(t);if(n!==s&&n!==c)return t;var r=new Map,i=e(t),o=P(0),u=null,d=qn,f=e=>{if(qn===d)return e();var t=U,n=qn;zn(null),Jn(d);var r=e();return zn(t),Jn(n),r};return i&&r.set(`length`,P(t.length,u)),new Proxy(t,{defineProperty(e,t,n){(!(`value`in n)||n.configurable===!1||n.enumerable===!1||n.writable===!1)&&Le();var i=r.get(t);return i===void 0?f(()=>{var e=P(n.value,u);return r.set(t,e),e}):F(i,n.value,!0),!0},deleteProperty(e,t){var n=r.get(t);if(n===void 0){if(t in e){let e=f(()=>P(D,u));r.set(t,e),Kt(o)}}else F(n,D),Kt(o);return!0},get(e,n,i){if(n===re)return t;var o=r.get(n),s=n in e;if(o===void 0&&(!s||a(e,n)?.writable)&&(o=f(()=>P(I(s?e[n]:D),u)),r.set(n,o)),o!==void 0){var c=q(o);return c===D?void 0:c}return Reflect.get(e,n,i)},getOwnPropertyDescriptor(e,t){var n=Reflect.getOwnPropertyDescriptor(e,t);if(n&&`value`in n){var i=r.get(t);i&&(n.value=q(i))}else if(n===void 0){var a=r.get(t),o=a?.v;if(a!==void 0&&o!==D)return{enumerable:!0,configurable:!0,value:o,writable:!0}}return n},has(e,t){if(t===re)return!0;var n=r.get(t),i=n!==void 0&&n.v!==D||Reflect.has(e,t);return(n!==void 0||W!==null&&(!i||a(e,t)?.writable))&&(n===void 0&&(n=f(()=>P(i?I(e[t]):D,u)),r.set(t,n)),q(n)===D)?!1:i},set(e,t,n,s){var c=r.get(t),l=t in e;if(i&&t===`length`)for(var d=n;d<c.v;d+=1){var p=r.get(d+``);p===void 0?d in e&&(p=f(()=>P(D,u)),r.set(d+``,p)):F(p,D)}if(c===void 0)(!l||a(e,t)?.writable)&&(c=f(()=>P(void 0,u)),F(c,I(n)),r.set(t,c));else{l=c.v!==D;var m=f(()=>I(n));F(c,m)}var h=Reflect.getOwnPropertyDescriptor(e,t);if(h?.set&&h.set.call(s,n),!l){if(i&&typeof t==`string`){var g=r.get(`length`),_=Number(t);Number.isInteger(_)&&_>=g.v&&F(g,_+1)}Kt(o)}return!0},ownKeys(e){q(o);var t=Reflect.ownKeys(e).filter(e=>{var t=r.get(e);return t===void 0||t.v!==D});for(var[n,i]of r)i.v!==D&&!(n in e)&&t.push(n);return t},setPrototypeOf(){Re()}})}function Jt(e){try{if(typeof e==`object`&&e&&re in e)return e[re]}catch{}return e}function Yt(e,t){return Object.is(Jt(e),Jt(t))}var Xt,Zt,Qt,$t;function en(){if(Xt===void 0){Xt=window,Zt=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;Qt=a(t,`firstChild`).get,$t=a(t,`nextSibling`).get,u(e)&&(e[ce]=void 0,e[se]=null,e[le]=void 0,e.__e=void 0),u(n)&&(n[ue]=void 0)}}function tn(e=``){return document.createTextNode(e)}function nn(e){return Qt.call(e)}function rn(e){return $t.call(e)}function L(e,t){if(!O)return nn(e);var n=nn(k);if(n===null)n=k.appendChild(tn());else if(t&&n.nodeType!==3){var r=tn();return n?.before(r),xe(r),r}return t&&ln(n),xe(n),n}function an(e,t=!1){if(!O){var n=nn(e);return n instanceof Comment&&n.data===``?rn(n):n}if(t){if(k?.nodeType!==3){var r=tn();return k?.before(r),xe(r),r}ln(k)}return k}function R(e,t=!1){if(!O)return nn(e);var n=L(e,t);return A(e),n}function z(e,t=1,n=!1){let r=O?k:e;for(var i;t--;)i=r,r=rn(r);if(!O)return r;if(n){if(r?.nodeType!==3){var a=tn();return r===null?i?.after(a):r.before(a),xe(a),a}ln(r)}return xe(r),r}function on(e){e.textContent=``}function sn(){return!1}function cn(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function ln(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function un(e){var t=W;if(t===null)return U.f|=ne,e;if(!(t.f&32768)&&!(t.f&4))throw e;dn(e,t)}function dn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function fn(e){W===null&&(U===null&&Pe(e),Ne()),In&&Me(e)}function pn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function mn(e,t){var n=W;n!==null&&n.f&8192&&(e|=v);var r={ctx:j,deps:null,nodes:null,f:e|g|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};N?.register_created_effect(r);var i=r;if(e&4)Dt===null?jt.ensure().schedule(r):Dt.push(r);else if(t!==null){try{nr(r)}catch(e){throw H(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=S))}if(i!==null&&(i.parent=n,n!==null&&pn(i,n),U!==null&&U.f&2&&!(e&64))){var a=U;(a.effects??=[]).push(i)}return r}function hn(){return U!==null&&!Rn}function gn(e){let t=mn(8,null);return M(t,h),t.teardown=e,t}function _n(e){fn(`$effect`);var t=W.f;if(!U&&t&32&&j!==null&&!j.i){var n=j;(n.e??=[]).push(e)}else return vn(e)}function vn(e){return mn(4|w,e)}function yn(e){jt.ensure();let t=mn(64|C,e);return(e={})=>new Promise(n=>{e.outro?kn(t,()=>{H(t),n(void 0)}):(H(t),n(void 0))})}function bn(e){return mn(4,e)}function xn(e){return mn(te|C,e)}function Sn(e,t=0){return mn(8|t,e)}function B(e,t=[],n=[],r=[]){st(r,t,n,t=>{mn(8,()=>{e(...t.map(q))})})}function Cn(e,t=0){return mn(16|t,e)}function V(e){return mn(32|C,e)}function wn(e){var t=e.teardown;if(t!==null){let n=In,r=U;Ln(!0),zn(null);try{t.call(null)}catch(t){dn(t,e.parent)}finally{Ln(n),zn(r)}}}function Tn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(fe)});var r=n.next;n.f&64?n.parent=null:H(n,t),n=r}}function En(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||H(t),t=n}}function H(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Dn(e.nodes.start,e.nodes.end),n=!0),e.f|=x,Tn(e,t&&!n),tr(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();wn(e),e.f^=x,e.f|=y;var i=e.parent;i!==null&&i.first!==null&&On(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Dn(e,t){for(;e!==null;){var n=e===t?null:rn(e);e.remove(),e=n}}function On(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function kn(e,t,n=!0){var r=[];e.f|=256,An(e,r,!0);var i=()=>{n&&H(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function An(e,t,n){if(!(e.f&8192)){e.f^=v;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);An(i,t,o?n:!1)}i=a}}}function jn(e){e.f&=-257,Mn(e,!0)}function Mn(e,t){if(!(e.f&256)&&e.f&8192){e.f^=v,e.f&1024||(M(e,g),jt.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Mn(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Nn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:rn(n);t.append(n),n=i}}var Pn=null,Fn=!1,In=!1;function Ln(e){In=e}var U=null,Rn=!1;function zn(e){U=e}var W=null;function Bn(e){W=e}var Vn=null;function Hn(e){U!==null&&(Vn??=new Set).add(e)}var G=null,K=0,Un=null;function Wn(e){Un=e}var Gn=1,Kn=0,qn=Kn;function Jn(e){qn=e}function Yn(){return++Gn}function Xn(e){var t=e.f;if(t&2048)return!0;if(t&2&&(e.f&=~E),t&4096){for(var n=e.deps,r=n.length,i=0;i<r;i++){var a=n[i];if(Xn(a)&&vt(a),a.wv>e.wv)return!0}t&512&&Ct===null&&M(e,h)}return!1}function Zn(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Vn!==null&&Vn.has(e)))for(var i=0;i<r.length;i++){var a=r[i];a.f&2?Zn(a,t,!1):t===a&&(n?M(a,g):a.f&1024&&M(a,_),It(a))}}function Qn(e){var t=G,n=K,r=Un,i=U,a=Vn,o=j,s=Rn,c=qn,l=e.f;G=null,K=0,Un=null,U=l&96?null:e,Vn=null,Ve(e.ctx),Rn=!1,qn=++Kn,e.ac!==null&&(at(()=>{e.ac.abort(fe)}),e.ac=null);try{e.f|=ee;var u=e.fn,d=u();e.f|=b;var f=$n(e);if(Ge()&&Un!==null&&!Rn&&f!==null&&!(e.f&6146))for(var p=0;p<Un.length;p++)Zn(Un[p],e);if(i!==null&&i!==e){if(Kn++,i.deps!==null)for(let e=0;e<n;e+=1)i.deps[e].rv=Kn;if(t!==null)for(let e of t)e.rv=Kn;Un!==null&&(r===null?r=Un:r.push(...Un))}return e.f&8388608&&(e.f^=ne),d}catch(t){return $n(e),un(t)}finally{e.f^=ee,G=t,K=n,Un=r,U=i,Vn=a,Ve(o),Rn=s,qn=c}}function $n(e){var t=e.deps,n=N?.is_fork;if(G!==null){var r;if(n||tr(e,K),t!==null&&K>0)for(t.length=K+G.length,r=0;r<G.length;r++)t[K+r]=G[r];else e.deps=t=G;if(hn()&&e.f&512)for(r=K;r<t.length;r++)(t[r].reactions??=[]).push(e)}else!n&&t!==null&&K<t.length&&(tr(e,K),t.length=K);return t}function er(e,r){let i=r.reactions;if(i!==null){var a=t.call(i,e);if(a!==-1){var o=i.length-1;o===0?i=r.reactions=null:(i[a]=i[o],i.pop())}}if(i===null&&r.f&2&&(G===null||!n.call(G,r))){var s=r;s.f&512&&(s.f^=512,s.f&=~E),s.v!==D&&Ze(s),s.ac!==null&&at(()=>{s.ac.abort(fe),s.ac=null,M(s,g)}),yt(s),tr(s,0)}}function tr(e,t){var n=e.deps;if(n!==null)for(var r=t;r<n.length;r++)er(e,n[r])}function nr(e){var t=e.f;if(!(t&16384)){M(e,h);var n=W,r=Fn;W=e,Fn=!(t&96);try{t&16777232?En(e):Tn(e),wn(e);var i=Qn(e);e.teardown=typeof i==`function`?i:null,e.wv=Gn}finally{Fn=r,W=n}}}async function rr(){await Promise.resolve(),Mt()}function q(e){var t=!!(e.f&2);if(Pn?.add(e),U!==null&&!Rn&&!(W!==null&&W.f&16384)&&(Vn===null||!Vn.has(e))){var r=U.deps;if(U.f&2097152)e.rv<Kn&&(e.rv=Kn,G===null&&r!==null&&r[K]===e?K++:G===null?G=[e]:G.push(e));else{U.deps??=[],n.call(U.deps,e)||U.deps.push(e);var i=e.reactions;i===null?e.reactions=[U]:n.call(i,U)||i.push(U)}}if(In&&Bt.has(e))return Bt.get(e);if(t){var a=e;if(In){var o=a.v;return(!(a.f&1024)&&a.reactions!==null||ar(a))&&(o=_t(a)),Bt.set(a,o),o}var s=!(a.f&512)&&!Rn&&U!==null&&(Fn||!!(U.f&512)),c=(a.f&b)===0;Xn(a)&&(s&&(a.f|=512),vt(a)),s&&!c&&(bt(a),ir(a))}if(Ct?.has(e))return Ct.get(e);if(e.f&8388608)throw e.v;return e.v}function ir(e){if(e.f|=512,e.deps!==null)for(let t of e.deps)(t.reactions??=[]).push(e),t.f&2&&!(t.f&512)&&(bt(t),ir(t))}function ar(e){if(e.v===D)return!0;if(e.deps===null)return!1;for(let t of e.deps)if(Bt.has(t)||t.f&2&&ar(t))return!0;return!1}function J(e){var t=Rn;try{return Rn=!0,e()}finally{Rn=t}}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var or=[`touchstart`,`touchmove`];function sr(e){return or.includes(e)}var cr=Symbol(`events`),lr=new Set,ur=new Set;function dr(e,t,n,r={}){function i(e){if(r.capture||gr.call(t,e),!e.cancelBubble)return at(()=>n?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?Je(()=>{t.addEventListener(e,i,r)}):t.addEventListener(e,i,r),i}function fr(e,t,n,r,i){var a={capture:r,passive:i},o=dr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&gn(()=>{t.removeEventListener(e,o,a)})}function Y(e,t,n){(t[cr]??={})[e]=n}function pr(e){for(var t=0;t<e.length;t++)lr.add(e[t]);for(var n of ur)n(e)}var mr=null,hr=!1;function gr(e){var t=this,n=t.ownerDocument,r=e.type,a=e.composedPath?.()||[],o=a[0]||e.target;mr=e,hr||(hr=!0,setTimeout(()=>{hr=!1,mr=null}));var s=0,c=mr===e&&e[cr];if(c){var l=a.indexOf(c);if(l!==-1&&(t===document||t===window)){e[cr]=t;return}var u=a.indexOf(t);if(u===-1)return;l<=u&&(s=l)}if(o=a[s]||e.target,o!==t){i(e,`currentTarget`,{configurable:!0,get(){return o||n}});var d=U,f=W;zn(null),Bn(null);try{for(var p,m=[];o!==null&&o!==t;){try{var h=o[cr]?.[r];h!=null&&(!o.disabled||e.target===o)&&h.call(o,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;s++,o=s<a.length?a[s]:null}if(p){for(let e of m)queueMicrotask(()=>{throw e});throw p}}finally{e[cr]=t,delete e.currentTarget,zn(d),Bn(f)}}}var _r=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function vr(e){return _r?.createHTML(e)??e}function yr(e){var t=cn(`template`);return t.innerHTML=vr(e.replaceAll(`<!>`,`<!---->`)),t.content}function br(e,t){var n=W;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function X(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(`<!>`);return()=>{if(O)return br(k,null),k;i===void 0&&(i=yr(a?e:`<!>`+e),n||(i=nn(i)));var t=r||Zt?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=nn(t),s=t.lastChild;br(o,s)}else br(t,t);return t}}function xr(){if(O)return br(k,null),k;var e=document.createDocumentFragment(),t=document.createComment(``),n=tn();return e.append(t,n),br(t,n),e}function Z(e,t){if(O){var n=W;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=k),Se();return}e!==null&&e.before(t)}function Sr(e){let t=0,n=Ht(0),r;return()=>{hn()&&(q(n),Sn(()=>(t===0&&(r=J(()=>e(()=>Kt(n)))),t+=1,()=>{Je(()=>{--t,t===0&&(r?.(),r=void 0,Kt(n))})})))}}var Cr=S|C;function wr(e,t,n,r){new Tr(e,t,n,r)}var Tr=class{parent;is_pending=!1;transform_error;#e;#t=O?k:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Sr(()=>(this.#m=Ht(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=W;t.b=this,t.f|=128,n(e)},this.parent=W.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Cn(()=>{if(O){let e=this.#t;Se();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Cr),O&&(this.#e=k)}#g(){try{this.#a=V(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Je(r),t&&(this.#s=V(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){ye();return}t=!0,n&&Be(),this.#s!==null&&kn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){dn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=V(()=>e(this.#e)),Je(()=>{var e=this.#c=document.createDocumentFragment(),t=tn(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return V(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){dn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(N);return}this.#u===0&&(this.#e.before(e),this.#c=null,kn(this.#o,()=>{this.#o=null}),this.#x(N))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=V(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Nn(this.#a,e);let t=this.#n.pending;this.#o=V(()=>t(this.#e))}else this.#x(N)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){$e(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=W,n=U,r=j;Bn(this.#i),zn(this.#i),Ve(this.#i.ctx);try{return jt.ensure(),e()}finally{Bn(t),zn(n),Ve(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&kn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Je(()=>{this.#d=!1,this.#m&&Wt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),q(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;N?.is_fork?(this.#a&&N.skip_effect(this.#a),this.#o&&N.skip_effect(this.#o),this.#s&&N.skip_effect(this.#s),N.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(H(this.#a),null),this.#o&&=(H(this.#o),null),this.#s&&=(H(this.#s),null),O&&(xe(this.#t),Ce(),xe(we()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return V(()=>{var r=W;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return dn(e,this.#i.parent),null}}))};Je(()=>{var t;try{t=this.transform_error(e)}catch(e){dn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>dn(e,this.#i&&this.#i.parent)):n(t)})}};function Q(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ue]??=e.nodeValue)&&(e[ue]=n,e.nodeValue=`${n}`)}function Er(e,t){return Or(e,t)}var Dr=new Map;function Or(e,{target:t,anchor:n,props:i={},events:a,context:o,intro:s=!0,transformError:c}){en();var l=void 0,u=yn(()=>{var s=n??t.appendChild(tn());wr(s,{pending:()=>{}},t=>{He({});var n=j;if(o&&(n.c=o),a&&(i.$$events=a),O&&br(t,null),l=e(t,i)||We(),O&&(W.nodes.end=k,k===null||k.nodeType!==8||k.data!==`]`))throw _e(),me;Ue()},c);var u=new Set,d=e=>{for(var n=0;n<e.length;n++){var r=e[n];if(!u.has(r)){u.add(r);var i=sr(r);for(let e of[t,document]){var a=Dr.get(e);a===void 0&&(a=new Map,Dr.set(e,a));var o=a.get(r);o===void 0?(e.addEventListener(r,gr,{passive:i}),a.set(r,1)):a.set(r,o+1)}}}};return d(r(lr)),ur.add(d),()=>{for(var e of u)for(let n of[t,document]){var r=Dr.get(n),i=r.get(e);--i==0?(n.removeEventListener(e,gr),r.delete(e),r.size===0&&Dr.delete(n)):r.set(e,i)}ur.delete(d),s!==n&&s.parentNode?.removeChild(s)}});return kr.set(l,u),l}var kr=new WeakMap,Ar=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)jn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(jn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(H(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Nn(r,t),t.append(tn()),this.#n.set(e,{effect:r,fragment:t})}else H(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),kn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(H(n.effect),this.#n.delete(e))};ensure(e,t){var n=N,r=sn();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=tn();i.append(a),this.#n.set(e,{effect:V(()=>t(a)),fragment:i})}else this.#t.set(e,V(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else O&&(this.anchor=k),this.#a(n)}};function $(e,t,n=!1){var r;O&&(r=k,Se());var i=new Ar(e),a=n?S:0;function o(e,t){if(O){var n=Te(r);if(e!==parseInt(n.substring(1))){var a=we();xe(a),i.anchor=a,be(!1),i.ensure(e,t),be(!0);return}}i.ensure(e,t)}Cn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}function jr(e,t,n){for(var i=[],a=t.length,o,s=t.length,c=0;c<a;c++){let n=t[c];kn(n,()=>{if(o){if(o.pending.delete(n),o.done.add(n),o.pending.size===0){var t=e.outrogroups;Mr(e,r(o.done)),t.delete(o),t.size===0&&(e.outrogroups=null)}}else--s},!1)}if(s===0){var l=i.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;on(d),d.append(u),e.items.clear()}Mr(e,t,!l)}else o={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(o)}function Mr(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i<t.length;i++){var a=t[i];r?.has(a)?(a.f|=T,Nn(a,document.createDocumentFragment())):H(t[i],n)}}var Nr;function Pr(t,n,i,a,o,s=null){var c=t,l=new Map;if(n&4){var u=t;c=O?xe(nn(u)):u.appendChild(tn())}O&&Se();var d=null,f=ht(()=>{var t=i();return e(t)?t:t==null?[]:r(t)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Ir(v,p,c,n,a),d!==null&&(p.length===0?d.f&33554432?(d.f^=T,Rr(d,null,c)):jn(d):kn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Cn(()=>{p=q(f);var e=p.length;let t=!1;O&&Te(c)===`[!`!=(e===0)&&(c=we(),xe(c),be(!1),t=!0);for(var r=new Set,u=N,v=sn(),y=0;y<e;y+=1){O&&k.nodeType===8&&k.data===`]`&&(c=k,t=!0,be(!1));var b=p[y],x=a(b,y),S=h?null:l.get(x);S?(S.v&&Wt(S.v,b),S.i&&Wt(S.i,y),v&&u.unskip_effect(S.e)):(S=Lr(l,h?c:Nr??=tn(),b,x,y,o,n,i),h||(S.e.f|=T),l.set(x,S)),r.add(x)}if(e===0&&s&&!d&&(h?d=V(()=>s(c)):(d=V(()=>s(Nr??=tn())),d.f|=T)),e>r.size&&je(``,``,``),O&&e>0&&xe(we()),!h){if(m.set(u,r),v){for(let[e,t]of l)r.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}t&&be(!0),q(f)}),flags:n,items:l,pending:m,outrogroups:null,fallback:d};h=!1,O&&(c=k)}function Fr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Ir(e,t,n,i,a){var o=!!(i&8),s=t.length,c=e.items,l=Fr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(o)for(v=0;v<s;v+=1)h=t[v],g=a(h,v),_=c.get(g).e,_.f&33554432||(_.nodes?.a?.measure(),(f??=new Set).add(_));for(v=0;v<s;v+=1){if(h=t[v],g=a(h,v),_=c.get(g).e,e.outrogroups!==null)for(let t of e.outrogroups)t.pending.delete(_),t.done.delete(_);if(_.f&8192&&(jn(_),o&&(_.nodes?.a?.unfix(),(f??=new Set).delete(_))),_.f&33554432){if(_.f^=T,_===l)Rr(_,null,n);else{var y=d?d.next:l;_===e.effect.last&&(e.effect.last=_.prev),_.prev&&(_.prev.next=_.next),_.next&&(_.next.prev=_.prev),zr(e,d,_),zr(e,_,y),Rr(_,y,n),d=_,p=[],m=[],l=Fr(d.next);continue}}if(_!==l){if(u!==void 0&&u.has(_)){if(p.length<m.length){var b=m[0],x;d=b.prev;var S=p[0],C=p[p.length-1];for(x=0;x<p.length;x+=1)Rr(p[x],b,n);for(x=0;x<m.length;x+=1)u.delete(m[x]);zr(e,S.prev,C.next),zr(e,d,S),zr(e,C,b),l=b,d=C,--v,p=[],m=[]}else u.delete(_),Rr(_,l,n),zr(e,_.prev,_.next),zr(e,_,d===null?e.effect.first:d.next),zr(e,d,_),d=_;continue}for(p=[],m=[];l!==null&&l!==_;)(u??=new Set).add(l),m.push(l),l=Fr(l.next);if(l===null)continue}_.f&33554432||p.push(_),d=_,l=Fr(_.next)}if(e.outrogroups!==null){for(let t of e.outrogroups)t.pending.size===0&&(Mr(e,r(t.done)),e.outrogroups?.delete(t));e.outrogroups.size===0&&(e.outrogroups=null)}if(l!==null||u!==void 0){var w=[];if(u!==void 0)for(_ of u)_.f&8192||w.push(_);for(;l!==null;)!(l.f&8192)&&l!==e.fallback&&w.push(l),l=Fr(l.next);var E=w.length;if(E>0){var ee=i&4&&s===0?n:null;if(o){for(v=0;v<E;v+=1)w[v].nodes?.a?.measure();for(v=0;v<E;v+=1)w[v].nodes?.a?.fix()}jr(e,w,ee)}}o&&Je(()=>{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Lr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Ht(n):Ut(n,!1,!1):null,l=o&2?Ht(i):null;return{v:c,i:l,e:V(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Rr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=rn(r);if(a.before(r),r===i)return;r=o}}function zr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}var Br=[...`
|
|
2
|
+
\r\f\xA0\v`];function Vr(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||Br.includes(r[o-1]))&&(s===r.length||Br.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function Hr(e,t,n,r,i,a){var o=e[ce];if(O||o!==n||o===void 0){var s=Vr(n,r,a);(!O||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[ce]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function Ur(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function Wr(t,n){var r=t.__defaultValue,i=t.multiple,a=i?r??[]:null;if(!(i&&!e(a))){var o=t.selectedIndex,s=n&&i?new Set(t.selectedOptions):null;for(var c of t.options){var l=Jr(c);Ur(c,i?a.includes(l):Yt(l,r))}if(n){if(s!==null)for(c of t.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else t.selectedIndex!==o&&(t.selectedIndex=o)}}}function Gr(t,n,r=!1){if(t.multiple){if(n==null)return;if(!e(n))return ve();for(var i of t.options)i.selected=n.includes(Jr(i));return}for(i of t.options)if(Yt(Jr(i),n)){i.selected=!0;return}(!r||n!==void 0)&&(t.selectedIndex=-1)}function Kr(e){var t=new MutationObserver(t=>{t.every(Yr)||(`__defaultValue`in e&&Wr(e,!1),`__value`in e&&Gr(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),gn(()=>{t.disconnect()})}function qr(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),Jr);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&Jr(o)}n(a),e.__value=a,N!==null&&r.add(N)}),bn(()=>{var a=t();if(e===document.activeElement){var o=N;if(r.has(o))return}if(Gr(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=Jr(s),n(a))}e.__value=a,i=!1})}function Jr(e){return`__value`in e?e.__value:e.value}function Yr(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var Xr=Symbol(`is custom element`),Zr=Symbol(`is html`),Qr=pe?`link`:`LINK`;function $r(e){if(O){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;ti(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;ti(e,`checked`,null),e.checked=r}}};e[de]=n,Je(n),it()}}function ei(e,t){var n=ni(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function ti(e,t,n,r){var i=ni(e);O&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===Qr)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[oe]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&ii(e).has(t)?e[t]=n:e.setAttribute(t,n))}function ni(e){return e[se]??={[Xr]:e.nodeName.includes(`-`),[Zr]:e.namespaceURI===he}}var ri=new Map;function ii(e){var t=e.getAttribute(`is`)||e.nodeName,n=ri.get(t);if(n)return n;ri.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var s in r=o(i),r)r[s].set&&s!==`innerHTML`&&s!==`textContent`&&s!==`innerText`&&n.add(s);i=l(i)}return n}function ai(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=si(e)?ci(a):a,n(a),N!==null&&r.add(N),await rr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(O&&e.defaultValue!==e.value||J(t)==null&&e.value)&&(n(si(e)?ci(e.value):e.value),N!==null&&r.add(N)),Sn(()=>{var n=t();if(e===document.activeElement){var i=N;if(r.has(i))return}si(e)&&n===ci(e.value)||e.type===`date`&&!n&&!e.value||n!==e.value&&(e.value=n??``)})}function oi(e,t,n=t){ot(e,`change`,t=>{n(t?e.defaultChecked:e.checked)}),(O&&e.defaultChecked!==e.checked||J(t)==null)&&n(e.checked),Sn(()=>{e.checked=!!t()})}function si(e){var t=e.type;return t===`number`||t===`range`}function ci(e){return e===``?null:+e}function li(e,t,n,r){var i=!0,o=!!(n&8),s=!!(n&16),c=r,l=!0,u=void 0,d=()=>s&&i?(u??=dt(r),q(u)):(l&&(l=!1,c=s?J(r):r),c);let f;if(o){var p=re in e||ae in e;f=a(e,t)?.set??(p&&t in e?n=>e[t]=n:void 0)}var m,h=!1;o?[m,h]=tt(()=>e[t]):m=e[t],m===void 0&&r!==void 0&&(m=d(),f&&(i&&Ie(t),f(m)));var g=i?()=>{var n=e[t];return n===void 0?d():(l=!0,n)}:()=>{var n=e[t];return n!==void 0&&(c=void 0),n===void 0?c:n};if(i&&!(n&4))return g;if(f){var _=e.$$legacy;return(function(e,t){return arguments.length>0?((!i||!t||_||h)&&f(t?g():e),e):g()})}var v=!1,y=(n&1?dt:ht)(()=>(v=!1,g()));o&&q(y);var b=W;return(function(e,t){if(arguments.length>0){let n=t?q(y):i&&o?I(e):e;return F(y,n),v=!0,c!==void 0&&(c=n),e}return In&&v||b.f&16384?y.v:q(y)})}function ui(e){j===null&&ke(`onMount`),_n(()=>{let t=J(e);if(typeof t==`function`)return t})}typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var di=X(`<button>Edit this disc</button>`),fi=X(`<button>Add this disc</button>`),pi=X(`<div><div><h4> </h4> <p> </p></div> <!></div>`);function mi(e,t){He(t,!0);let n=P(null);_n(()=>{let e=new EventSource(`/api/v1/current-tag/events`);return e.onmessage=e=>{F(n,JSON.parse(e.data),!0)},()=>e.close()});var r=xr(),i=an(r),a=e=>{var r=pi();let i;var a=L(r),o=L(a),s=R(o,!0),c=R(z(o,2));A(a);var l=z(a,2),u=e=>{var r=di();Y(`click`,r,()=>t.onEditDisc?.(q(n).tag_id)),Z(e,r)},d=e=>{var r=fi();Y(`click`,r,()=>t.onAddDisc?.(q(n).tag_id)),Z(e,r)};$(l,e=>{q(n).known_in_library?e(u):e(d,-1)}),A(r),B(()=>{i=Hr(r,1,`banner`,null,i,{"banner-info":q(n).known_in_library,"banner-warning":!q(n).known_in_library}),Q(s,q(n).known_in_library?`Known disc on reader`:`Unknown disc on reader`),Q(c,`Tag "${q(n).tag_id??``}" is
|
|
3
|
+
${q(n).known_in_library?`already in the library.`:`ready to be added to the library.`}`)}),Z(e,r)};$(i,e=>{q(n)&&e(a)}),Z(e,r),Ue()}pr([`click`]);var hi=`/api/v1`,gi=class extends Error{constructor(e,t){super(`API error ${e}`),this.status=e,this.body=t}};async function _i(e,t={}){let n=await fetch(`${hi}${e}`,{headers:{"Content-Type":`application/json`,...t.headers},...t});if(!n.ok)throw new gi(n.status,await n.json().catch(()=>null));return n.status===204?null:n.json()}function vi(e){return _i(e)}function yi(e,t){return _i(e,{method:`POST`,body:JSON.stringify(t)})}function bi(e,t){return _i(e,{method:`PUT`,body:JSON.stringify(t)})}function xi(e,t){return _i(e,{method:`PATCH`,body:JSON.stringify(t)})}function Si(e){return _i(e,{method:`DELETE`})}var Ci=X(`<p class="error"> </p>`),wi=X(`<form><label>Tag ID <input required=""/></label> <label>URI / Path <input required=""/></label> <label>Artist <input/></label> <label>Album <input/></label> <label>Track <input/></label> <label>Playlist <input/></label> <label class="checkbox"><input type="checkbox"/> Shuffle</label> <!> <div class="actions"><button type="submit" class="btn-primary"> </button> <button type="button" class="btn-secondary">Cancel</button></div></form>`);function Ti(e,t){He(t,!0);let n=li(t,`tagId`,3,``),r=li(t,`disc`,3,null),i=P(I(J(()=>n()))),a=P(I(J(()=>r()?.uri??``))),o=P(I(J(()=>r()?.metadata.artist??``))),s=P(I(J(()=>r()?.metadata.album??``))),c=P(I(J(()=>r()?.metadata.track??``))),l=P(I(J(()=>r()?.metadata.playlist??``))),u=P(I(J(()=>r()?.option.shuffle??!1))),d=P(null),f=P(!1);async function p(e){e.preventDefault(),F(f,!0),F(d,null);let n={uri:q(a),metadata:{artist:q(o),album:q(s),track:q(c),playlist:q(l)},option:{shuffle:q(u)}};try{t.mode===`create`?await yi(`/discs/${q(i)}`,n):await xi(`/discs/${q(i)}`,n),t.onSaved()}catch(e){F(d,e.body?.detail??e.message,!0)}finally{F(f,!1)}}var m=wi(),h=L(m),g=z(L(h));$r(g),A(h);var _=z(h,2),v=z(L(_));$r(v),A(_);var y=z(_,2),b=z(L(y));$r(b),A(y);var x=z(y,2),S=z(L(x));$r(S),A(x);var C=z(x,2),w=z(L(C));$r(w),A(C);var T=z(C,2),E=z(L(T));$r(E),A(T);var ee=z(T,2),te=L(ee);$r(te),Ce(),A(ee);var ne=z(ee,2),re=e=>{var t=Ci(),n=R(t,!0);B(()=>Q(n,q(d))),Z(e,t)};$(ne,e=>{q(d)&&e(re)});var ie=z(ne,2),ae=L(ie),oe=R(ae,!0),se=z(ae,2);A(ie),A(m),B(()=>{g.disabled=t.mode===`edit`,ae.disabled=q(f),Q(oe,q(f)?`Saving…`:`Save`)}),fr(`submit`,m,p),ai(g,()=>q(i),e=>F(i,e)),ai(v,()=>q(a),e=>F(a,e)),ai(b,()=>q(o),e=>F(o,e)),ai(S,()=>q(s),e=>F(s,e)),ai(w,()=>q(c),e=>F(c,e)),ai(E,()=>q(l),e=>F(l,e)),oi(te,()=>q(u),e=>F(u,e)),Y(`click`,se,function(...e){t.onCancel?.apply(this,e)}),Z(e,m),Ue()}pr([`click`]);var Ei=X(`<button>Add disc</button>`),Di=X(`<p>Loading…</p>`),Oi=X(`<p class="error"> </p>`),ki=X(`<p>No disc found</p>`),Ai=X(`<span class="tag-spin" aria-hidden="true">💿</span>`),ji=X(`<tr><td class="spin-cell"><!></td><td class="tag"> </td><td class="uri"><span class="uri-row"><span class="uri-text"> </span> <button type="button" class="uri-copy"> </button></span></td><td class="type"><span class="type-icon"> </span> <span class="type-label"> </span></td><td class="title"> </td><td class="center"><span role="img">🔀</span></td><td class="row-actions"><button>Edit</button> <button class="btn-danger">Delete</button></td></tr>`),Mi=X(`<!> <table class="discs"><colgroup><col style="width: 24px"/><col style="width: 14%"/><col/><col style="width: 90px"/><col style="width: 28%"/><col style="width: 70px"/><col style="width: 130px"/></colgroup><thead><tr><th></th><th>Tag</th><th>URI</th><th>Type</th><th>Title</th><th>Shuffle</th><th></th></tr></thead><tbody></tbody></table>`,1),Ni=X(`<div class="page-header"><h2>Library</h2> <!></div> <!>`,1);function Pi(e,t){He(t,!0);let n=li(t,`intent`,3,null),r=P(I({})),i=P(!0),a=P(null),o=P(null),s=P(null),c=P(null),l=P(null);_n(()=>{let e=new EventSource(`/api/v1/current-tag/events`);return e.onmessage=e=>{let t=JSON.parse(e.data);F(s,t?.known_in_library?t.tag_id:null,!0)},()=>e.close()});async function u(){F(i,!0);try{F(r,await vi(`/discs`),!0),F(a,null)}catch(e){F(a,e.message,!0)}finally{F(i,!1)}}ui(u),_n(()=>{!n()||q(i)||(n().type===`edit`?f(n().tagId):d(n().tagId),t.onIntentConsumed?.())});function d(e=``){F(o,{type:`create`,tagId:e},!0)}function f(e){F(o,{type:`edit`,tagId:e,disc:q(r)[e]},!0)}function p(){F(o,null)}async function h(){F(o,null),await u()}async function g(e,t){await navigator.clipboard.writeText(t),F(c,e,!0),setTimeout(()=>{q(c)===e&&F(c,null)},1500)}function _(e){return e.split(` `)[0]}function v(e){let t=e.indexOf(` `);return t===-1?``:e.slice(t+1)}async function y(e){if(confirm(`Delete disc "${e}"?`))try{await Si(`/discs/${e}`),F(l,null),await u()}catch(e){F(l,e.body?.detail??e.message,!0)}}var b=Ni(),x=an(b),S=z(L(x),2),C=e=>{var t=Ei();Y(`click`,t,()=>d()),Z(e,t)};$(S,e=>{q(o)||e(C)}),A(x);var w=z(x,2),T=e=>{Ti(e,{mode:`create`,get tagId(){return q(o).tagId},onSaved:h,onCancel:p})},E=e=>{Ti(e,{mode:`edit`,get tagId(){return q(o).tagId},get disc(){return q(o).disc},onSaved:h,onCancel:p})},ee=e=>{var t=xr(),n=an(t),o=e=>{Z(e,Di())},u=e=>{var t=Oi(),n=R(t,!0);B(()=>Q(n,q(a))),Z(e,t)},d=e=>{Z(e,ki())},p=mt(()=>Object.keys(q(r)).length===0),h=e=>{var t=Mi(),n=an(t),i=e=>{var t=Oi(),n=R(t,!0);B(()=>Q(n,q(l))),Z(e,t)};$(n,e=>{q(l)&&e(i)});var a=z(n,2),o=z(L(a),2);Pr(o,21,()=>Object.entries(q(r)),([e,t])=>e,(e,t)=>{var n=mt(()=>m(q(t),2));let r=()=>q(n)[0],i=()=>q(n)[1];var a=ji(),o=L(a),l=L(o),u=e=>{Z(e,Ai())};$(l,e=>{r()===q(s)&&e(u)}),A(o);var d=z(o),p=R(d,!0),h=z(d),b=L(h),x=L(b),S=R(x,!0),C=z(x,2),w=R(C,!0);A(b),A(h);var T=z(h),E=L(T),ee=R(E,!0),te=R(z(E,2),!0);A(T);var ne=z(T),re=R(ne,!0),ie=z(ne),ae=L(ie);let oe;A(ie);var se=z(ie),ce=L(se),le=z(ce,2);A(se),A(a),B((e,t)=>{Q(p,r()),ti(h,`title`,i().uri),Q(S,i().uri),ti(C,`aria-label`,q(c)===r()?`Copied`:`Copy URI`),Q(w,q(c)===r()?`✅`:`📋`),Q(ee,e),Q(te,t),Q(re,i().display_title),oe=Hr(ae,1,`shuffle-icon`,null,oe,{active:i().option.shuffle}),ti(ae,`aria-label`,i().option.shuffle?`Shuffle on`:`Shuffle off`)},[()=>_(i().display_type),()=>v(i().display_type)]),Y(`click`,C,()=>g(r(),i().uri)),Y(`click`,ce,()=>f(r())),Y(`click`,le,()=>y(r())),Z(e,a)}),A(o),A(a),Z(e,t)};$(n,e=>{q(i)?e(o):q(a)?e(u,1):q(p)?e(d,2):e(h,-1)}),Z(e,t)};$(w,e=>{q(o)?.type===`create`?e(T):q(o)?.type===`edit`?e(E,1):e(ee,-1)}),Z(e,b),Ue()}pr([`click`]);var Fi=X(`<option> </option>`),Ii=X(`<select></select>`),Li=X(`<textarea rows="10" placeholder="Enter a JSON object. Leave blank to persist null."></textarea>`),Ri=X(`<input/>`),zi=X(`<p class="error"> </p>`),Bi=X(`<button type="button">Reset</button>`),Vi=X(`<form><h3> </h3> <p class="path"> </p> <p> </p> <!> <!> <div class="actions"><button type="submit"> </button> <!> <button type="button">Cancel</button></div></form>`);function Hi(e,t){He(t,!0);function n(e){let t=e.is_persisted?e.persisted_value:e.effective_value;return t==null?``:e.field_type===`object`?JSON.stringify(t,null,2):String(t)}let r=P(I(J(()=>n(t.setting)))),i=P(null),a=P(!1);function o(e,t){let n=e.split(`.`),r={},i=r;for(let e of n.slice(0,-1))i[e]={},i=i[e];return i[n.at(-1)]=t,r}function s(){if(t.setting.choices.length>0)return q(r);if(t.setting.field_type===`integer`){let e=Number.parseInt(q(r),10);if(Number.isNaN(e))throw Error(`Enter a valid integer.`);return e}if(t.setting.field_type===`number`){let e=Number.parseFloat(q(r));if(Number.isNaN(e))throw Error(`Enter a valid number.`);return e}if(t.setting.field_type===`object`){if(q(r).trim()===``)return null;try{return JSON.parse(q(r))}catch{throw Error(`Enter valid JSON.`)}}return q(r)}async function c(e){e.preventDefault(),F(i,null),F(a,!0);try{let e=s();await xi(`/settings`,o(t.setting.path,e)),t.onSaved()}catch(e){F(i,e.body?.detail??e.message,!0)}finally{F(a,!1)}}async function l(){F(i,null),F(a,!0);try{await yi(`/settings/reset`,{path:t.setting.path}),t.onSaved()}catch(e){F(i,e.body?.detail??e.message,!0)}finally{F(a,!1)}}var u=Vi(),d=L(u),f=R(d),p=z(d,2),m=R(p,!0),h=z(p,2),g=R(h,!0),_=z(h,2),v=e=>{var n=Ii();Pr(n,21,()=>t.setting.choices,e=>e.value,(e,t)=>{var n=Fi(),r=R(n,!0),i={};B(()=>{Q(r,q(t).label),i!==(i=q(t).value)&&(n.value=(n.__value=i)??``)}),Z(e,n)}),A(n),Kr(n),qr(n,()=>q(r),e=>F(r,e)),Z(e,n)},y=e=>{var t=Li();nt(t),ai(t,()=>q(r),e=>F(r,e)),Z(e,t)},b=e=>{var n=Ri();$r(n),B(()=>{ti(n,`type`,t.setting.field_type===`integer`||t.setting.field_type===`number`?`number`:`text`),ti(n,`step`,t.setting.field_type===`number`?`any`:void 0)}),ai(n,()=>q(r),e=>F(r,e)),Z(e,n)};$(_,e=>{t.setting.choices.length>0?e(v):t.setting.field_type===`object`?e(y,1):e(b,-1)});var x=z(_,2),S=e=>{var t=zi(),n=R(t,!0);B(()=>Q(n,q(i))),Z(e,t)};$(x,e=>{q(i)&&e(S)});var C=z(x,2),w=L(C),T=R(w,!0),E=z(w,2),ee=e=>{var t=Bi();B(()=>t.disabled=q(a)),Y(`click`,t,l),Z(e,t)};$(E,e=>{t.setting.is_persisted&&e(ee)});var te=z(E,2);A(C),A(u),B(()=>{Q(f,`Edit ${t.setting.label??``}`),Q(m,t.setting.path),Q(g,t.setting.description),w.disabled=q(a),Q(T,q(a)?`Saving…`:`Save`)}),fr(`submit`,u,c),Y(`click`,te,function(...e){t.onCancel?.apply(this,e)}),Z(e,u),Ue()}pr([`click`]);var Ui=X(`<p>Loading…</p>`),Wi=X(`<p class="error"> </p>`),Gi=X(`<tr><td><strong> </strong> <div class="path"> </div></td><td> </td><td> </td><td><button>Edit</button></td></tr>`),Ki=X(`<h3> </h3> <table><thead><tr><th>Setting</th><th>Effective value</th><th>Source</th><th></th></tr></thead><tbody></tbody></table>`,1),qi=X(`<!> <!>`,1),Ji=X(`<h2>Settings</h2> <!>`,1);function Yi(e,t){He(t,!0);let n=P(I([])),r=P(null),i=P(!0),a=P(null),o=P(null);async function s(){F(i,!0);try{let e=await vi(`/settings/displays`);F(n,e.settings,!0),F(r,e.effective_settings_error,!0),F(a,null)}catch(e){F(a,e.message,!0)}finally{F(i,!1)}}ui(s);function c(e){let t=[];for(let n of e){let e=t.at(-1);e&&e.section===n.section?e.entries.push(n):t.push({section:n.section,label:n.section_label,entries:[n]})}return t}function l(e){return e==null?`null`:typeof e==`object`?JSON.stringify(e):String(e)}let u=mt(()=>c(q(n))),d=mt(()=>q(n).find(e=>e.path===q(o))??null);function f(e){F(o,e,!0)}function p(){F(o,null)}async function m(){F(o,null),await s()}var h=Ji(),g=z(an(h),2),_=e=>{Hi(e,{get setting(){return q(d)},onSaved:m,onCancel:p})},v=e=>{Z(e,Ui())},y=e=>{var t=Wi(),n=R(t,!0);B(()=>Q(n,q(a))),Z(e,t)},b=e=>{var t=qi(),n=an(t),i=e=>{var t=Wi(),n=R(t);B(()=>Q(n,`${q(r)??``} Persisted overrides are still shown below.`)),Z(e,t)};$(n,e=>{q(r)&&e(i)}),Pr(z(n,2),17,()=>q(u),e=>e.section,(e,t)=>{var n=Ki(),r=an(n),i=R(r,!0),a=z(r,2),o=z(L(a));Pr(o,21,()=>q(t).entries,e=>e.path,(e,t)=>{var n=Gi(),r=L(n),i=L(r),a=R(i,!0),o=R(z(i,2),!0);A(r);var s=z(r),c=R(s,!0),u=z(s),d=R(u,!0),p=R(z(u));A(n),B(e=>{Q(a,q(t).label),Q(o,q(t).path),Q(c,e),Q(d,q(t).provenance)},[()=>l(q(t).effective_value)]),Y(`click`,p,()=>f(q(t).path)),Z(e,n)}),A(o),A(a),B(()=>Q(i,q(t).label)),Z(e,n)}),Z(e,t)};$(g,e=>{q(d)?e(_):q(i)?e(v,1):q(a)?e(y,2):e(b,-1)}),Z(e,h),Ue()}pr([`click`]);var Xi=X(`<label class="checkbox"><input type="checkbox"/> </label>`),Zi=X(`<option> </option>`),Qi=X(`<p class="error"> </p>`),$i=X(`<form><h3>Edit Sonos Selection</h3> <p>Choose one or more visible speakers and select the coordinator used for playback.</p> <p>Changes take effect after restart.</p> <fieldset><legend>Speakers</legend> <!></fieldset> <label>Coordinator <select></select></label> <!> <div class="actions"><button type="submit"> </button> <button type="button">Cancel</button></div></form>`);function ea(e,t){He(t,!0);function n(){return t.selectedGroup?t.selectedGroup.members.map(e=>e.uid):t.speakers[0]?[t.speakers[0].uid]:[]}let r=P(I(J(()=>new Set(n())))),i=P(I(J(()=>t.selectedGroup?.coordinator_uid??n()[0]??``))),a=P(null),o=P(!1);function s(e){let t=new Set(q(r));t.has(e)?t.delete(e):t.add(e),F(r,t,!0)}async function c(e){e.preventDefault(),F(a,null),F(o,!0);try{await bi(`/sonos/selection`,{uids:[...q(r)],coordinator_uid:q(i)}),t.onSaved()}catch(e){F(a,e.body?.detail??e.message,!0)}finally{F(o,!1)}}var l=$i(),u=z(L(l),6);Pr(z(L(u),2),17,()=>t.speakers,e=>e.uid,(e,t)=>{var n=Xi(),i=L(n);$r(i);var a=z(i);A(n),B(e=>{ei(i,e),Q(a,` ${q(t).name??``} (${q(t).host??``})`)},[()=>q(r).has(q(t).uid)]),Y(`change`,i,()=>s(q(t).uid)),Z(e,n)}),A(u);var d=z(u,2),f=z(L(d));Pr(f,21,()=>t.speakers,e=>e.uid,(e,t)=>{var n=Zi(),r=R(n),i={};B(()=>{Q(r,`${q(t).name??``} (${q(t).host??``})`),i!==(i=q(t).uid)&&(n.value=(n.__value=i)??``)}),Z(e,n)}),A(f),Kr(f),A(d);var p=z(d,2),m=e=>{var t=Qi(),n=R(t,!0);B(()=>Q(n,q(a))),Z(e,t)};$(p,e=>{q(a)&&e(m)});var h=z(p,2),g=L(h),_=R(g,!0),v=z(g,2);A(h),A(l),B(()=>{g.disabled=q(o),Q(_,q(o)?`Saving…`:`Save`)}),fr(`submit`,l,c),qr(f,()=>q(i),e=>F(i,e)),Y(`click`,v,function(...e){t.onCancel?.apply(this,e)}),Z(e,l),Ue()}pr([`change`,`click`]);var ta=X(`<p>Loading…</p>`),na=X(`<p class="error"> </p>`),ra=X(`<p>No Sonos speaker selection is currently saved.</p>`),ia=X(`<p> </p> <p> </p> <p> </p>`,1),aa=X(`<button class="btn-danger">Clear saved selection</button>`),oa=X(`<p>No visible Sonos speakers found.</p>`),sa=X(`<tr><td> </td><td> </td><td> </td><td class="center"> </td></tr>`),ca=X(`<table><thead><tr><th>Name</th><th>Host</th><th>Household</th><th>Selection</th></tr></thead><tbody></tbody></table>`),la=X(`<h3>Discovered speakers</h3> <!>`,1),ua=X(`<!> <div class="panel"><h3>Saved selection</h3> <!></div> <div class="row-actions"><button>Edit selection</button> <!></div> <!>`,1),da=X(`<h2>Sonos</h2> <!>`,1);function fa(e,t){He(t,!0);let n={available:`Available`,partial:`Partially available`,unavailable:`Unavailable`,not_selected:`Not selected`},r=P(null),i=P(I([])),a=P(!0),o=P(null),s=P(null),c=P(!1);async function l(){F(a,!0),F(o,null),F(s,null);try{F(r,await vi(`/sonos/selection`),!0),F(i,await vi(`/sonos/speakers`),!0)}catch(e){e instanceof gi&&e.status===502?F(o,e.body?.detail??`Sonos discovery unavailable.`,!0):F(s,e.message,!0)}finally{F(a,!1)}}ui(l);let u=mt(()=>Object.fromEntries(q(i).map(e=>[e.uid,e])));function d(e){let t=q(u)[e];return t?`${t.name} [${e}]`:e}function f(e){let t=q(r)?.selected_group;return t?t.coordinator_uid===e?`Coordinator`:t.members.some(t=>t.uid===e)?`Selected`:`Available`:`Available`}function p(){F(c,!0)}function m(){F(c,!1)}async function h(){F(c,!1),await l()}async function g(){await yi(`/settings/reset`,{path:`jukebox.player.sonos.selected_group`}),await l()}var _=da(),v=z(an(_),2),y=e=>{{let t=mt(()=>q(r)?.selected_group??null);ea(e,{get speakers(){return q(i)},get selectedGroup(){return q(t)},onSaved:h,onCancel:m})}},b=e=>{Z(e,ta())},x=e=>{var t=na(),n=R(t,!0);B(()=>Q(n,q(s))),Z(e,t)},S=e=>{var t=ua(),a=an(t),s=e=>{var t=na(),n=R(t);B(()=>Q(n,`Sonos discovery unavailable: ${q(o)??``}`)),Z(e,t)};$(a,e=>{q(o)&&e(s)});var c=z(a,2),l=z(L(c),2),u=e=>{Z(e,ra())},m=e=>{var t=ia(),i=an(t),a=R(i),o=z(i,2),s=R(o),c=R(z(o,2));B((e,t)=>{Q(a,`Status: ${n[q(r).availability.status]??q(r).availability.status??``}`),Q(s,`Coordinator: ${e??``}`),Q(c,`Members: ${t??``}`)},[()=>d(q(r).selected_group.coordinator_uid),()=>q(r).selected_group.members.map(e=>d(e.uid)).join(`, `)]),Z(e,t)};$(l,e=>{q(r)?.selected_group?e(m,-1):e(u)}),A(c);var h=z(c,2),_=L(h),v=z(_,2),y=e=>{var t=aa();Y(`click`,t,g),Z(e,t)};$(v,e=>{q(r)?.selected_group&&e(y)}),A(h);var b=z(h,2),x=e=>{var t=la(),n=z(an(t),2),r=e=>{Z(e,oa())},a=e=>{var t=ca(),n=z(L(t));Pr(n,21,()=>q(i),e=>e.uid,(e,t)=>{var n=sa(),r=L(n),i=R(r,!0),a=z(r),o=R(a,!0),s=z(a),c=R(s,!0),l=R(z(s),!0);A(n),B(e=>{Q(i,q(t).name),Q(o,q(t).host),Q(c,q(t).household_id),Q(l,e)},[()=>f(q(t).uid)]),Z(e,n)}),A(n),A(t),Z(e,t)};$(n,e=>{q(i).length===0?e(r):e(a,-1)}),Z(e,t)};$(b,e=>{q(o)||e(x)}),Y(`click`,_,p),Z(e,t)};$(v,e=>{q(c)?e(y):q(a)?e(b,1):q(s)?e(x,2):e(S,-1)}),Z(e,_),Ue()}pr([`click`]);var pa=X(`<button> </button>`),ma=X(`<!> <nav></nav> <main><!></main> <footer><a href="https://github.com/Gudsfile/jukebox" target="_blank" rel="noopener noreferrer">Jukebox made with ❤️ & 💿 · View on GitHub</a></footer>`,1);function ha(e){let t=[`library`,`settings`,`sonos`],n=P(`library`),r=P(null);function i(e){F(r,e,!0),F(n,`library`)}function a(){F(r,null)}var o=ma(),s=an(o);mi(s,{onEditDisc:e=>i({type:`edit`,tagId:e}),onAddDisc:e=>i({type:`create`,tagId:e})});var c=z(s,2);Pr(c,20,()=>t,e=>e,(e,t)=>{var r=pa();let i;var a=R(r,!0);B(()=>{i=Hr(r,1,``,null,i,{active:q(n)===t}),Q(a,t)}),Y(`click`,r,()=>F(n,t,!0)),Z(e,r)}),A(c);var l=z(c,2),u=L(l),d=e=>{Pi(e,{get intent(){return q(r)},onIntentConsumed:a})},f=e=>{Yi(e,{})},p=e=>{fa(e,{})};$(u,e=>{q(n)===`library`?e(d):q(n)===`settings`?e(f,1):q(n)===`sonos`&&e(p,2)}),A(l),Ce(2),Z(e,o)}pr([`click`]),Er(ha,{target:document.getElementById(`app`)});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--text:#6b6153;--text-h:#1e1912;--bg:#fbf6ec;--border:#e4dcc9;--accent:#a6690e;--accent-bg:#a6690e1a;--danger:#a83324;--state-known:#1f8f80;--state-known-bg:#1f8f801f;--state-unknown:#93307d;--state-unknown-bg:#93307d1f;--sans:system-ui, "Segoe UI", Roboto, sans-serif;--mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;font:16px/145% var(--sans);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;color:var(--text);background:var(--bg)}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial;--text:#a79e8e;--text-h:#f2ece1;--bg:#17140f;--border:#3a3327;--accent:#e8a33d;--accent-bg:#e8a33d26;--danger:#c4432e;--state-known:#2fb8a6;--state-known-bg:#2fb8a626;--state-unknown:#b3489a;--state-unknown-bg:#b3489a26}}html,body{height:100%;margin:0}#app{flex-direction:column;min-height:100%;display:flex}h1,h2{color:var(--text-h);font-weight:500}.page-header{justify-content:space-between;align-items:center;gap:12px;margin-bottom:16px;display:flex}.page-header h2{margin:0}button{font:inherit;color:var(--bg);background:var(--text-h);border:1px solid var(--text-h);cursor:pointer;border-radius:6px;padding:6px 14px}button:hover{opacity:.85}button:disabled{opacity:.5;cursor:not-allowed}button.btn-danger{color:#fff;background:var(--danger);border-color:var(--danger)}button.btn-primary{color:#fff;background:var(--accent);border-color:var(--accent)}button.btn-secondary{color:var(--text);border-color:var(--border);background:0 0}nav{border-bottom:1px solid var(--border);flex-wrap:wrap;gap:8px;padding:12px 20px;display:flex}nav button{color:inherit;border-color:var(--border);text-transform:capitalize;background:0 0}nav button.active{color:var(--accent);background:var(--accent-bg);border-color:var(--accent)}main{flex:1;padding:20px}footer{text-align:center;padding:16px 20px 24px;font-size:.75em}footer a{color:var(--text);text-decoration:none;transition:color .2s}footer a:hover{color:var(--text-h)}.row-actions{white-space:nowrap;gap:6px;display:flex}.center{text-align:center}.type{white-space:nowrap}.type-label{margin-left:4px}.tag{font-family:var(--mono);color:var(--text);font-size:.85em}.spin-cell{text-align:center}.tag-spin{animation:2s linear infinite spin;display:inline-block}@media (prefers-reduced-motion:reduce){.tag-spin{animation:none}}@keyframes spin{to{transform:rotate(360deg)}}.title{text-align:center;min-width:180px}@media (width<=480px){.type-label{display:none}.uri-text{max-width:3ch}}.shuffle-icon{opacity:.3;filter:grayscale()}.shuffle-icon.active{opacity:1;filter:none}.uri{font-family:var(--mono);color:var(--text);font-size:.85em}.uri-row{align-items:center;gap:4px;max-width:100%;display:inline-flex}.uri-text{text-overflow:ellipsis;white-space:nowrap;max-width:40ch;display:block;overflow:hidden}.uri-copy{color:var(--text);background:0 0;border:none;border-radius:0;flex-shrink:0;padding:0 2px;font-size:.9em;line-height:1}.uri-copy:hover{color:var(--accent);opacity:1}table.discs{table-layout:fixed}.banner{border-bottom:1px solid var(--border);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;padding:12px 20px;display:flex}.banner h4{color:var(--text-h);margin:0}.banner p{margin:4px 0 0}.banner-info{background:var(--state-known-bg);border-left:3px solid var(--state-known)}.banner-warning{background:var(--state-unknown-bg);border-left:3px solid var(--state-unknown)}.error{color:#d33}.path{color:var(--text);opacity:.75;font-size:.85em}table{border-collapse:collapse;width:100%;margin-bottom:16px;display:block;overflow-x:auto}th,td{text-align:left;border-bottom:1px solid var(--border);padding:6px 10px}form{max-width:480px}form>label,form>.checkbox{margin-bottom:12px;display:block}label{color:var(--text-h);font-weight:500}input,select,textarea{width:100%;font:inherit;color:inherit;background:var(--bg);border:1px solid var(--border);box-sizing:border-box;border-radius:6px;margin-top:4px;padding:6px 10px;display:block}fieldset{border:1px solid var(--border);border-radius:6px;margin:0 0 12px;padding:10px 14px}legend{color:var(--text-h);padding:0 4px;font-weight:500}.checkbox{align-items:center;gap:8px;margin-bottom:6px;font-weight:400;display:flex}.checkbox input{width:auto;margin:0;display:inline}.panel{border:1px solid var(--border);border-radius:6px;margin-bottom:16px;padding:12px 16px}.panel h3{margin-top:0}.actions{gap:8px;margin-top:12px;display:flex}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/ui/favicon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Jukebox Admin</title>
|
|
8
|
+
<script type="module" crossorigin src="/ui/assets/index-B2mMrbHV.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/ui/assets/index-BhofQAra.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -225,7 +225,8 @@ class SoCoSonosDiscoveryAdapter(SonosDiscoveryPort):
|
|
|
225
225
|
for speaker in list(discovered):
|
|
226
226
|
try:
|
|
227
227
|
available_speakers.update(speaker.all_zones)
|
|
228
|
-
except _SONOS_TRANSPORT_ERRORS:
|
|
228
|
+
except _SONOS_TRANSPORT_ERRORS as exc:
|
|
229
|
+
LOGGER.debug("Failed to expand zones for %s: %s", speaker, exc)
|
|
229
230
|
available_speakers.add(speaker)
|
|
230
231
|
|
|
231
232
|
if not available_speakers:
|
|
@@ -350,7 +351,7 @@ def _safe_speaker_identifier(speaker: "_SonosSpeakerLike") -> str:
|
|
|
350
351
|
|
|
351
352
|
def _safe_speaker_host(speaker: "_SonosSpeakerLike") -> str | None:
|
|
352
353
|
try:
|
|
353
|
-
ip_address =
|
|
354
|
+
ip_address = speaker.ip_address
|
|
354
355
|
except _SONOS_TRANSPORT_ERRORS:
|
|
355
356
|
return None
|
|
356
357
|
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import traceback
|
|
3
|
+
from contextlib import contextmanager
|
|
3
4
|
from typing import Annotated, Never
|
|
4
5
|
|
|
5
6
|
import typer
|
|
6
7
|
from pydantic import ValidationError
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.status import Status
|
|
7
10
|
|
|
8
11
|
from jukebox.admin.library_command_handlers import execute_library_command
|
|
9
12
|
from jukebox.admin.library_commands import (
|
|
@@ -108,15 +111,16 @@ def _run_command(ctx: typer.Context, command: object) -> None:
|
|
|
108
111
|
)
|
|
109
112
|
try:
|
|
110
113
|
if is_sonos_command(command):
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
with _sonos_command_status() as (status_update, status):
|
|
115
|
+
execute_sonos_command(
|
|
116
|
+
command=command,
|
|
117
|
+
sonos_service=services.sonos,
|
|
118
|
+
settings_service=services.settings,
|
|
119
|
+
household_prompt_fn=_pausing_for_prompt(status, _prompt_for_sonos_household_selection),
|
|
120
|
+
speaker_prompt_fn=_pausing_for_prompt(status, _prompt_for_sonos_speaker_selection),
|
|
121
|
+
coordinator_prompt_fn=_pausing_for_prompt(status, _prompt_for_sonos_group_coordinator),
|
|
122
|
+
status_fn=status_update,
|
|
123
|
+
)
|
|
120
124
|
elif is_pn532_command(command):
|
|
121
125
|
execute_pn532_command(
|
|
122
126
|
command=command,
|
|
@@ -284,10 +288,27 @@ def _prompt_for_sonos_group_coordinator(speakers: list[DiscoveredSonosSpeaker])
|
|
|
284
288
|
return None
|
|
285
289
|
|
|
286
290
|
|
|
287
|
-
|
|
291
|
+
@contextmanager
|
|
292
|
+
def _sonos_command_status():
|
|
288
293
|
if not sys.stderr.isatty():
|
|
294
|
+
yield None, None
|
|
289
295
|
return
|
|
290
|
-
|
|
296
|
+
console = Console(stderr=True)
|
|
297
|
+
with console.status("Preparing...", spinner="dots") as status:
|
|
298
|
+
yield status.update, status
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _pausing_for_prompt(status: Status | None, prompt_fn):
|
|
302
|
+
def wrapped(*args, **kwargs):
|
|
303
|
+
if status is not None:
|
|
304
|
+
status.stop()
|
|
305
|
+
try:
|
|
306
|
+
return prompt_fn(*args, **kwargs)
|
|
307
|
+
finally:
|
|
308
|
+
if status is not None:
|
|
309
|
+
status.start()
|
|
310
|
+
|
|
311
|
+
return wrapped
|
|
291
312
|
|
|
292
313
|
|
|
293
314
|
app = typer.Typer(help="Admin CLI for jukebox")
|
|
@@ -167,6 +167,7 @@ def execute_sonos_command(
|
|
|
167
167
|
if settings_service is None:
|
|
168
168
|
raise TypeError("settings_service is required for Sonos show commands")
|
|
169
169
|
|
|
170
|
+
_emit_status(status_fn, "Checking Sonos selection...")
|
|
170
171
|
status = GetSonosSelectionStatus(
|
|
171
172
|
selected_group_repository=SettingsSelectedSonosGroupRepository(settings_service),
|
|
172
173
|
sonos_service=sonos_service,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Literal, Self
|
|
1
|
+
from typing import Literal, Self
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, model_validator
|
|
4
4
|
|
|
@@ -53,33 +53,5 @@ class SettingsResetCommand(BaseModel):
|
|
|
53
53
|
json_output: bool = False
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
AdminCommand: TypeAlias = (
|
|
57
|
-
ApiCommand
|
|
58
|
-
| SettingsResetCommand
|
|
59
|
-
| SettingsSetCommand
|
|
60
|
-
| SettingsShowCommand
|
|
61
|
-
| SonosListCommand
|
|
62
|
-
| SonosSelectCommand
|
|
63
|
-
| SonosShowCommand
|
|
64
|
-
| UiCommand
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def is_admin_command(command: object) -> bool:
|
|
69
|
-
return isinstance(
|
|
70
|
-
command,
|
|
71
|
-
(
|
|
72
|
-
ApiCommand,
|
|
73
|
-
SettingsResetCommand,
|
|
74
|
-
SettingsSetCommand,
|
|
75
|
-
SettingsShowCommand,
|
|
76
|
-
SonosListCommand,
|
|
77
|
-
SonosSelectCommand,
|
|
78
|
-
SonosShowCommand,
|
|
79
|
-
UiCommand,
|
|
80
|
-
),
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
|
|
84
56
|
def is_sonos_command(command: object) -> bool:
|
|
85
57
|
return isinstance(command, (SonosListCommand, SonosSelectCommand, SonosShowCommand))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def set_logger(logger_name: str, verbose: bool = False):
|
|
5
|
+
level = logging.DEBUG if verbose else logging.INFO
|
|
6
|
+
logger = logging.getLogger(logger_name)
|
|
7
|
+
logger.setLevel(level)
|
|
8
|
+
logger.propagate = False
|
|
9
|
+
if not logger.handlers:
|
|
10
|
+
console_handler = logging.StreamHandler()
|
|
11
|
+
console_handler.setLevel(level)
|
|
12
|
+
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s\t - %(message)s")
|
|
13
|
+
console_handler.setFormatter(formatter)
|
|
14
|
+
logger.addHandler(console_handler)
|
|
15
|
+
return logger
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gukebox"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.5.0.dev1"
|
|
4
4
|
description = "A Jukebox to play music on speakers using 'CD' with NFC tag"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11,<3.14"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"pydantic==2.13.
|
|
8
|
+
"pydantic==2.13.5",
|
|
9
9
|
"questionary==2.1.1",
|
|
10
|
-
"soco==0.31.
|
|
11
|
-
"typer==0.27.
|
|
10
|
+
"soco==0.31.2",
|
|
11
|
+
"typer==0.27.2",
|
|
12
12
|
]
|
|
13
13
|
classifiers = [
|
|
14
14
|
"Development Status :: 4 - Beta",
|
|
@@ -36,8 +36,8 @@ Repository = "https://github.com/Gudsfile/jukebox"
|
|
|
36
36
|
|
|
37
37
|
[project.optional-dependencies]
|
|
38
38
|
api = [
|
|
39
|
-
"fastapi==0.
|
|
40
|
-
"uvicorn==0.
|
|
39
|
+
"fastapi==0.141.1",
|
|
40
|
+
"uvicorn==0.52.4",
|
|
41
41
|
]
|
|
42
42
|
ui = [
|
|
43
43
|
"gukebox[api]",
|
|
@@ -59,8 +59,8 @@ jukebox-admin = "jukebox.admin.app:main"
|
|
|
59
59
|
dev = [
|
|
60
60
|
"pytest==9.1.1",
|
|
61
61
|
"pytest-mock==3.15.1",
|
|
62
|
-
"ruff==0.16.
|
|
63
|
-
"ty==0.0.
|
|
62
|
+
"ruff==0.16.5",
|
|
63
|
+
"ty==0.0.75",
|
|
64
64
|
]
|
|
65
65
|
|
|
66
66
|
[tool.ruff]
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gukebox"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.5.0.dev1"
|
|
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"
|
|
7
7
|
license = { file = "LICENSE" }
|
|
8
8
|
requires-python = ">=3.11,<3.14"
|
|
9
9
|
dependencies = [
|
|
10
|
-
"pydantic==2.13.
|
|
10
|
+
"pydantic==2.13.5",
|
|
11
11
|
"questionary==2.1.1",
|
|
12
|
-
"soco==0.31.
|
|
13
|
-
"typer==0.27.
|
|
12
|
+
"soco==0.31.2",
|
|
13
|
+
"typer==0.27.2",
|
|
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.141.1",
|
|
32
|
+
"uvicorn==0.52.4",
|
|
33
33
|
]
|
|
34
34
|
ui = [
|
|
35
35
|
"gukebox[api]",
|
|
@@ -47,8 +47,8 @@ pn532 = [
|
|
|
47
47
|
dev = [
|
|
48
48
|
"pytest==9.1.1",
|
|
49
49
|
"pytest-mock==3.15.1",
|
|
50
|
-
"ruff==0.16.
|
|
51
|
-
"ty==0.0.
|
|
50
|
+
"ruff==0.16.5",
|
|
51
|
+
"ty==0.0.75",
|
|
52
52
|
]
|
|
53
53
|
|
|
54
54
|
[tool.ruff]
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from pydantic import BaseModel, RootModel
|
|
4
|
-
|
|
5
|
-
from jukebox.domain.entities import CurrentTagStatus, Disc
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class DiscInput(Disc):
|
|
9
|
-
pass
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class DiscOutput(Disc):
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class DiscPatchMetadataInput(BaseModel):
|
|
17
|
-
artist: str | None = None
|
|
18
|
-
album: str | None = None
|
|
19
|
-
track: str | None = None
|
|
20
|
-
playlist: str | None = None
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class DiscPatchOptionInput(BaseModel):
|
|
24
|
-
shuffle: bool | None = None
|
|
25
|
-
is_test: bool | None = None
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class DiscPatchInput(BaseModel):
|
|
29
|
-
uri: str | None = None
|
|
30
|
-
metadata: DiscPatchMetadataInput | None = None
|
|
31
|
-
option: DiscPatchOptionInput | None = None
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class CurrentTagStatusOutput(CurrentTagStatus):
|
|
35
|
-
pass
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class CurrentTagDiscOutput(BaseModel):
|
|
39
|
-
tag_id: str
|
|
40
|
-
disc: DiscOutput
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class SettingsResetInput(BaseModel):
|
|
44
|
-
path: str
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class SettingsPatchInput(RootModel[dict[str, Any]]):
|
|
48
|
-
pass
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def set_logger(logger_name: str, verbose: bool = False):
|
|
5
|
-
level = logging.DEBUG if verbose else logging.INFO
|
|
6
|
-
logger = logging.getLogger(logger_name)
|
|
7
|
-
logger.setLevel(level)
|
|
8
|
-
console_handler = logging.StreamHandler()
|
|
9
|
-
console_handler.setLevel(level)
|
|
10
|
-
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s\t - %(message)s")
|
|
11
|
-
console_handler.setFormatter(formatter)
|
|
12
|
-
logger.addHandler(console_handler)
|
|
13
|
-
return logger
|
|
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.2 → gukebox-1.5.0.dev1}/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
|
|
File without changes
|
|
File without changes
|
{gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/players/dryrun_player_adapter.py
RENAMED
|
File without changes
|
{gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/players/sonos_player_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
{gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/readers/dryrun_reader_adapter.py
RENAMED
|
File without changes
|
{gukebox-1.4.2 → gukebox-1.5.0.dev1}/jukebox/adapters/outbound/readers/pn532_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
|
|
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
|
{gukebox-1.4.2 → gukebox-1.5.0.dev1}/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
|
|
File without changes
|
|
File without changes
|