gukebox 1.4.3__tar.gz → 1.5.0.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/PKG-INFO +6 -6
  2. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/api/current_tag_router.py +41 -1
  3. gukebox-1.5.0.dev2/jukebox/adapters/inbound/admin/api/models.py +86 -0
  4. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/api/settings_router.py +33 -1
  5. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/api_controller.py +13 -0
  6. gukebox-1.5.0.dev2/jukebox/adapters/inbound/admin/static/assets/index-CYMkUb9q.css +1 -0
  7. gukebox-1.5.0.dev2/jukebox/adapters/inbound/admin/static/assets/index-S2QaCKBl.js +3 -0
  8. gukebox-1.5.0.dev2/jukebox/adapters/inbound/admin/static/favicon.svg +1 -0
  9. gukebox-1.5.0.dev2/jukebox/adapters/inbound/admin/static/index.html +14 -0
  10. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/app.py +32 -11
  11. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/command_handlers.py +1 -0
  12. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/sonos/selection.py +13 -2
  13. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/pyproject.toml +8 -8
  14. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/pyproject.toml.orig +8 -8
  15. gukebox-1.4.3/jukebox/adapters/inbound/admin/api/models.py +0 -48
  16. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/LICENSE +0 -0
  17. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/README.md +0 -0
  18. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/__init__.py +0 -0
  19. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/__init__.py +0 -0
  20. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/__init__.py +0 -0
  21. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/__init__.py +0 -0
  22. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/api/__init__.py +0 -0
  23. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/api/discs_router.py +0 -0
  24. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/cli_controller.py +0 -0
  25. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/cli_display.py +0 -0
  26. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/interactive_cli_controller.py +0 -0
  27. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/ui_controller.py +0 -0
  28. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/ui_pages/__init__.py +0 -0
  29. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/ui_pages/library.py +0 -0
  30. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/ui_pages/settings.py +0 -0
  31. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/admin/ui_pages/sonos.py +0 -0
  32. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/inbound/cli_controller.py +0 -0
  33. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/__init__.py +0 -0
  34. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/json_library_adapter.py +0 -0
  35. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/players/__init__.py +0 -0
  36. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/players/dryrun_player_adapter.py +0 -0
  37. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/players/sonos_player_adapter.py +0 -0
  38. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/readers/__init__.py +0 -0
  39. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/readers/dryrun_reader_adapter.py +0 -0
  40. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/readers/pn532_reader_adapter.py +0 -0
  41. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/sonos_discovery_adapter.py +0 -0
  42. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/adapters/outbound/text_current_tag_adapter.py +0 -0
  43. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/__init__.py +0 -0
  44. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/cli_presentation.py +0 -0
  45. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/commands.py +0 -0
  46. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/di_container.py +0 -0
  47. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/library_command_handlers.py +0 -0
  48. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/library_commands.py +0 -0
  49. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/pn532_command_handlers.py +0 -0
  50. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/pn532_commands.py +0 -0
  51. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/services.py +0 -0
  52. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/admin/sonos_households.py +0 -0
  53. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/app.py +0 -0
  54. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/di_container.py +0 -0
  55. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/__init__.py +0 -0
  56. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/entities/__init__.py +0 -0
  57. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/entities/current_tag_state.py +0 -0
  58. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/entities/current_tag_status.py +0 -0
  59. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/entities/disc.py +0 -0
  60. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/entities/library.py +0 -0
  61. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/entities/playback_state.py +0 -0
  62. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/entities/tag_event.py +0 -0
  63. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/errors.py +0 -0
  64. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/ports/__init__.py +0 -0
  65. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/ports/player_port.py +0 -0
  66. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/ports/reader_port.py +0 -0
  67. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/repositories/__init__.py +0 -0
  68. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/repositories/current_tag_repository.py +0 -0
  69. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/repositories/library_repository.py +0 -0
  70. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/__init__.py +0 -0
  71. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/handle_tag_event.py +0 -0
  72. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/library/__init__.py +0 -0
  73. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/library/add_disc.py +0 -0
  74. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/library/edit_disc.py +0 -0
  75. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/library/get_current_tag_status.py +0 -0
  76. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/library/get_disc.py +0 -0
  77. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/library/list_discs.py +0 -0
  78. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/library/remove_disc.py +0 -0
  79. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/library/resolve_tag_id.py +0 -0
  80. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/library/search_discs.py +0 -0
  81. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/sync_current_tag.py +0 -0
  82. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/transition_current_tag.py +0 -0
  83. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/domain/use_cases/transition_playback.py +0 -0
  84. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/pn532/__init__.py +0 -0
  85. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/pn532/profiles.py +0 -0
  86. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/__init__.py +0 -0
  87. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/definitions.py +0 -0
  88. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/dict_utils.py +0 -0
  89. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/entities.py +0 -0
  90. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/errors.py +0 -0
  91. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/file_settings_repository.py +0 -0
  92. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/migration.py +0 -0
  93. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/repositories.py +0 -0
  94. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/resolve.py +0 -0
  95. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/runtime_resolver.py +0 -0
  96. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/runtime_validation.py +0 -0
  97. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/selected_sonos_group_repository.py +0 -0
  98. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/service_protocols.py +0 -0
  99. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/timing_validation.py +0 -0
  100. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/types.py +0 -0
  101. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/validation_rules.py +0 -0
  102. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/settings/view_utils.py +0 -0
  103. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/shared/__init__.py +0 -0
  104. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/shared/config_utils.py +0 -0
  105. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/shared/errors.py +0 -0
  106. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/shared/logger.py +0 -0
  107. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/shared/terminal_ui.py +0 -0
  108. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/shared/timing.py +0 -0
  109. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/sonos/__init__.py +0 -0
  110. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/sonos/discovery.py +0 -0
  111. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/jukebox/sonos/service.py +0 -0
  112. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/pn532/__init__.py +0 -0
  113. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/pn532/pn532.py +0 -0
  114. {gukebox-1.4.3 → gukebox-1.5.0.dev2}/pn532/spi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: gukebox
3
- Version: 1.4.3
3
+ Version: 1.5.0.dev2
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.4
35
+ Requires-Dist: pydantic==2.13.5
36
36
  Requires-Dist: questionary==2.1.1
37
- Requires-Dist: soco==0.31.1
38
- Requires-Dist: typer==0.27.0
39
- Requires-Dist: fastapi==0.140.13 ; extra == 'api'
40
- Requires-Dist: uvicorn==0.51.0 ; extra == 'api'
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'
@@ -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,
@@ -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 SettingsPatchInput, SettingsResetInput
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 @@
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}.badge{white-space:nowrap;border:1px solid;border-radius:999px;margin-left:6px;padding:1px 6px;font-size:.7em;font-weight:600;line-height:1.4;display:inline-block}.badge-configured{color:var(--state-known)}.badge-pinned{color:var(--text)}.badge-restart{color:var(--danger)}.badge-advanced{color:var(--accent)}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,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 A(e){if(e===null)throw _e(),me;return k=e}function xe(){return A(tn(k))}function j(e){if(O){if(tn(k)!==null)throw _e(),me;k=e}}function Se(e=1){if(O){for(var t=e,n=k;t--;)n=tn(n);k=n}}function Ce(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=tn(n);e&&n.remove(),n=i}}function we(e){if(!e||e.nodeType!==8)throw _e(),me;return e.data}function Te(e){return e===this.v}function Ee(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function De(e){return!Ee(e,this.v)}function Oe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function ke(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Ae(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function je(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function Me(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Ne(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Pe(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function Fe(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ie(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function Le(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Re(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function ze(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var M=null;function Be(e){M=e}function Ve(e,t=!1,n){M={p:M,i:!1,c:null,e:null,s:e,x:null,r:G,l:null}}function He(e){var t=M,n=t.e;if(n!==null){t.e=null;for(var r of n)gn(r)}return e!==void 0&&(t.x=e),t.i=!0,M=t.p,Ue(e)}function Ue(e={}){return i(e,ie,{value:!0}),e}function We(){return!0}var Ge=[];function Ke(){var e=Ge;Ge=[],f(e)}function qe(e){if(Ge.length===0&&!wt){var t=Ge;queueMicrotask(()=>{t===Ge&&Ke()})}Ge.push(e)}function Je(){for(;Ge.length>0;)Ke()}var Ye=~(g|_|h);function N(e,t){e.f=e.f&Ye|t}function Xe(e){e.f&512||e.deps===null?N(e,h):N(e,_)}function Ze(e){if(e!==null)for(let t of e)!(t.f&2)||!(t.f&65536)||(t.f^=E,Ze(t.deps))}function Qe(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),Ze(e.deps),N(e,h)}var $e=!1;function et(e){var t=$e;try{return $e=!1,[e(),$e]}finally{$e=t}}function tt(e){O&&en(e)!==null&&rn(e)}var nt=!1;function rt(){nt||(nt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[de]?.()})},{capture:!0}))}function it(e){var t=W,n=G;Rn(null),zn(null);try{return e()}finally{Rn(t),zn(n)}}function at(e,t,n,r=n){e.addEventListener(t,()=>it(n));let i=e[de];e[de]=i?()=>{i(),r(!0)}:()=>r(!0),rt()}function ot(e,t,n,r){let i=We()?ut:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=G,c=st(),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){ln(e,s)}ct()}}var d=lt();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>ft(e))).then(u).catch(e=>ln(e,s)).finally(d)}l?l.then(()=>{c(),f(),ct()}):f()}function st(){var e=G,t=W,n=M,r=P;return function(i=!0){zn(e),Rn(t),Be(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function ct(e=!0){zn(null),Rn(null),Be(null),e&&P?.deactivate()}function lt(){var e=G,t=e.b,n=P,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 ut(e){var t=2|g;return G!==null&&(G.f|=C),{ctx:M,deps:null,effects:null,equals:Te,f:t,fn:e,reactions:null,rv:0,v:D,wv:0,parent:G,ac:null}}var dt=Symbol(`obsolete`);function ft(e,t,n){let r=G;r===null&&ke();var i=void 0,a=Vt(D),o=!W,s=new Set;return yn(()=>{var t=G,n=p();i=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==fe&&n.reject(e)}).finally(ct)}catch(e){n.reject(e),ct()}var c=P;if(o){if(t.f&32768)var l=lt();if(r.b?.is_rendered())c.async_deriveds.get(t)?.reject(dt);else for(let e of s.values())e.reject(dt);s.add(n),c.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),s.delete(n),t!==dt&&(c.activate(),t?(a.f|=ne,Ut(a,t)):(a.f&8388608&&(a.f^=ne),Ut(a,e)),c.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),mn(()=>{for(let e of s)e.reject(dt)}),new Promise(e=>{function t(n){function r(){n===i?e(a):t(i)}n.then(r,r)}t(i)})}function pt(e){let t=ut(e);return Vn(t),t}function mt(e){let t=ut(e);return t.equals=De,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n<t.length;n+=1)U(t[n])}}function gt(e){var t,n=G,r=e.parent;if(!Fn&&r!==null&&e.v!==D&&r.f&24576)return ge(),e.v;zn(r);try{e.f&=~E,ht(e),t=Qn(e)}finally{zn(n)}return t}function _t(e){var t=gt(e);if(!e.equals(t)&&(e.wv=Yn(),(!P?.is_fork||e.deps===null)&&(P===null?e.v=t:(P.capture(e,t,!0),xt?.capture(e,t,!0)),e.deps===null))){N(e,h);return}Fn||(St===null?Xe(e):(pn()||P?.is_fork)&&St.set(e,t))}function vt(e){if(e.effects!==null)for(let t of e.effects)(t.teardown||t.ac)&&(t.teardown?.(),t.ac!==null&&it(()=>{t.ac.abort(fe),t.ac=null}),t.fn!==null&&(t.teardown=d),tr(t,0),wn(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&nr(t)}var bt=null,P=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#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(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=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)N(r,g),t(r);for(r of n.m)N(r,_),t(r)}this.#p.add(e)}#g(){this.#e=!0,Ot++>1e3&&(this.#x(),Mt());for(let e of this.#u)this.#d.delete(e),N(e,g),this.schedule(e);for(let e of this.#d)N(e,_),this.schedule(e);let t=this.#c;this.#c=[],this.apply();var n=Et=[],r=[],i=Dt=[];for(let e of t)try{this.#_(e,n,r)}catch(t){throw Lt(e),this.#h()||this.discard(),t}if(P=null,i.length>0){var a=e.ensure();for(let e of i)a.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#b(r),this.#b(n);for(let[e,t]of this.#f)It(e,t);i.length>0&&P.#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(),xt=this,Pt(r),Pt(n),xt=null,this.#s?.resolve();var s=P;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&&(zt.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),N(i,g),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#x(),P=this,this.#g()}#b(e){for(var t=0;t<e.length;t+=1)Qe(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]),St?.set(e,t)),this.is_fork||(e.v=t)}activate(){P=this}deactivate(){P=null,St=null}flush(){try{Tt=!0,P=this,this.#g()}finally{Ot=0,Ct=null,Et=null,Dt=null,Tt=!1,P=null,St=null,zt.clear()}}discard(){for(let e of this.#i)e(this);this.#i.clear();for(let e of this.async_deriveds.values())e.reject(dt);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,qe(()=>{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(P===null){let t=P=new e;!Tt&&!wt&&qe(()=>{t.#e||t.flush()})}return P}apply(){St=null}schedule(e){if(Ct=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(Et!==null&&t===G&&(W===null||!(W.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?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(P!==null&&!P.is_fork&&P.flush(),n=e());;){if(Je(),P===null)return n;P.flush()}}finally{wt=t}}function Mt(){try{Pe()}catch(e){ln(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n<t;){var r=e[n++];if(!(r.f&24576)&&Xn(r)&&(Nt=new Set,nr(r),r.deps===null&&r.first===null&&r.nodes===null&&r.teardown===null&&r.ac===null&&Dn(r),Nt?.size>0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.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)}}Nt.clear()}}Nt=null}}function Ft(e){P.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),N(e,h);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){N(e,h);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Te,rv:0,wv:0}}function F(e,t){let n=Vt(e,t);return Vn(n),n}function Ht(e,t=!1,n=!0){let r=Vt(e);return t||(r.equals=De),r}function I(e,t,n=!1){return W!==null&&(!Ln||W.f&131072)&&We()&&W.f&4325394&&(Bn===null||!Bn.has(e))&&Re(),Ut(e,n?L(t):t,Dt)}function Ut(e,t,n=null){if(!e.equals(t)){Fn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&&gt(t),St===null&&Xe(t)}e.wv=Yn(),Kt(e,g,n),We()&&G!==null&&G.f&1024&&!(G.f&96)&&(Un===null?Wn([e]):Un.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Wt()}return t}function Wt(){Bt=!1;for(let e of Rt){e.f&1024&&N(e,_);let t;try{t=Xn(e)}catch{t=!0}t&&nr(e)}Rt.clear()}function Gt(e){I(e,e.v+1)}function Kt(e,t,n){var r=e.reactions;if(r!==null)for(var i=We(),a=r.length,o=0;o<a;o++){var s=r[o],c=s.f;if(!(!i&&s===G)){var l=(c&g)===0;if(l&&N(s,t),c&131072)Rt.add(s);else if(c&2){var u=s;St?.delete(u),c&65536||(c&512&&(G===null||!(G.f&2097152))&&(s.f|=E),Kt(u,_,n))}else if(l){var d=s;c&16&&Nt!==null&&Nt.add(d),n===null?Ft(d):n.push(d)}}}}function L(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=F(0),u=null,d=qn,f=e=>{if(qn===d)return e();var t=W,n=qn;Rn(null),Jn(d);var r=e();return Rn(t),Jn(n),r};return i&&r.set(`length`,F(t.length,u)),new Proxy(t,{defineProperty(e,t,n){(!(`value`in n)||n.configurable===!1||n.enumerable===!1||n.writable===!1)&&Ie();var i=r.get(t);return i===void 0?f(()=>{var e=F(n.value,u);return r.set(t,e),e}):I(i,n.value,!0),!0},deleteProperty(e,t){var n=r.get(t);if(n===void 0){if(t in e){let e=f(()=>F(D,u));r.set(t,e),Gt(o)}}else I(n,D),Gt(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(()=>F(L(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||G!==null&&(!i||a(e,t)?.writable))&&(n===void 0&&(n=f(()=>F(i?L(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(()=>F(D,u)),r.set(d+``,p)):I(p,D)}if(c===void 0)(!l||a(e,t)?.writable)&&(c=f(()=>F(void 0,u)),I(c,L(n)),r.set(t,c));else{l=c.v!==D;var m=f(()=>L(n));I(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&&I(g,_+1)}Gt(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(){Le()}})}function qt(e){try{if(typeof e==`object`&&e&&re in e)return e[re]}catch{}return e}function Jt(e,t){return Object.is(qt(e),qt(t))}var Yt,Xt,Zt,Qt;function $t(){if(Yt===void 0){Yt=window,Xt=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;Zt=a(t,`firstChild`).get,Qt=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 R(e=``){return document.createTextNode(e)}function en(e){return Zt.call(e)}function tn(e){return Qt.call(e)}function z(e,t){if(!O)return en(e);var n=en(k);if(n===null)n=k.appendChild(R());else if(t&&n.nodeType!==3){var r=R();return n?.before(r),A(r),r}return t&&sn(n),A(n),n}function nn(e,t=!1){if(!O){var n=en(e);return n instanceof Comment&&n.data===``?tn(n):n}if(t){if(k?.nodeType!==3){var r=R();return k?.before(r),A(r),r}sn(k)}return k}function B(e,t=!1){if(!O)return en(e);var n=z(e,t);return j(e),n}function V(e,t=1,n=!1){let r=O?k:e;for(var i;t--;)i=r,r=tn(r);if(!O)return r;if(n){if(r?.nodeType!==3){var a=R();return r===null?i?.after(a):r.before(a),A(a),a}sn(r)}return A(r),r}function rn(e){e.textContent=``}function an(){return!1}function on(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 sn(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 cn(e){var t=G;if(t===null)return W.f|=ne,e;if(!(t.f&32768)&&!(t.f&4))throw e;ln(e,t)}function ln(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 un(e){G===null&&(W===null&&Ne(e),Me()),Fn&&je(e)}function dn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function fn(e,t){var n=G;n!==null&&n.f&8192&&(e|=v);var r={ctx:M,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};P?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{nr(r)}catch(e){throw U(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&&dn(i,n),W!==null&&W.f&2&&!(e&64))){var a=W;(a.effects??=[]).push(i)}return r}function pn(){return W!==null&&!Ln}function mn(e){let t=fn(8,null);return N(t,h),t.teardown=e,t}function hn(e){un(`$effect`);var t=G.f;if(!W&&t&32&&M!==null&&!M.i){var n=M;(n.e??=[]).push(e)}else return gn(e)}function gn(e){return fn(4|w,e)}function _n(e){At.ensure();let t=fn(64|C,e);return(e={})=>new Promise(n=>{e.outro?On(t,()=>{U(t),n(void 0)}):(U(t),n(void 0))})}function vn(e){return fn(4,e)}function yn(e){return fn(te|C,e)}function bn(e,t=0){return fn(8|t,e)}function H(e,t=[],n=[],r=[]){ot(r,t,n,t=>{fn(8,()=>{e(...t.map(q))})})}function xn(e,t=0){return fn(16|t,e)}function Sn(e){return fn(32|C,e)}function Cn(e){var t=e.teardown;if(t!==null){let n=Fn,r=W;In(!0),Rn(null);try{t.call(null)}catch(t){ln(t,e.parent)}finally{In(n),Rn(r)}}}function wn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&it(()=>{e.abort(fe)});var r=n.next;n.f&64?n.parent=null:U(n,t),n=r}}function Tn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||U(t),t=n}}function U(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(En(e.nodes.start,e.nodes.end),n=!0),e.f|=x,wn(e,t&&!n),tr(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();Cn(e),e.f^=x,e.f|=y;var i=e.parent;i!==null&&i.first!==null&&Dn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function En(e,t){for(;e!==null;){var n=e===t?null:tn(e);e.remove(),e=n}}function Dn(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 On(e,t,n=!0){var r=[];e.f|=256,kn(e,r,!0);var i=()=>{n&&U(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function kn(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);kn(i,t,o?n:!1)}i=a}}}function An(e){e.f&=-257,jn(e,!0)}function jn(e,t){if(!(e.f&256)&&e.f&8192){e.f^=v,e.f&1024||(N(e,g),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);jn(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 Mn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:tn(n);t.append(n),n=i}}var Nn=null,Pn=!1,Fn=!1;function In(e){Fn=e}var W=null,Ln=!1;function Rn(e){W=e}var G=null;function zn(e){G=e}var Bn=null;function Vn(e){W!==null&&(Bn??=new Set).add(e)}var K=null,Hn=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)&&_t(a),a.wv>e.wv)return!0}t&512&&St===null&&N(e,h)}return!1}function Zn(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Bn!==null&&Bn.has(e)))for(var i=0;i<r.length;i++){var a=r[i];a.f&2?Zn(a,t,!1):t===a&&(n?N(a,g):a.f&1024&&N(a,_),Ft(a))}}function Qn(e){var t=K,n=Hn,r=Un,i=W,a=Bn,o=M,s=Ln,c=qn,l=e.f;K=null,Hn=0,Un=null,W=l&96?null:e,Bn=null,Be(e.ctx),Ln=!1,qn=++Kn,e.ac!==null&&(it(()=>{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(We()&&Un!==null&&!Ln&&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),cn(t)}finally{e.f^=ee,K=t,Hn=n,Un=r,W=i,Bn=a,Be(o),Ln=s,qn=c}}function $n(e){var t=e.deps,n=P?.is_fork;if(K!==null){var r;if(n||tr(e,Hn),t!==null&&Hn>0)for(t.length=Hn+K.length,r=0;r<K.length;r++)t[Hn+r]=K[r];else e.deps=t=K;if(pn()&&e.f&512)for(r=Hn;r<t.length;r++)(t[r].reactions??=[]).push(e)}else!n&&t!==null&&Hn<t.length&&(tr(e,Hn),t.length=Hn);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&&(K===null||!n.call(K,r))){var s=r;s.f&512&&(s.f^=512,s.f&=~E),s.v!==D&&Xe(s),s.ac!==null&&it(()=>{s.ac.abort(fe),s.ac=null,N(s,g)}),vt(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)){N(e,h);var n=G,r=Pn;G=e,Pn=!(t&96);try{t&16777232?Tn(e):wn(e),Cn(e);var i=Qn(e);e.teardown=typeof i==`function`?i:null,e.wv=Gn}finally{Pn=r,G=n}}}async function rr(){await Promise.resolve(),jt()}function q(e){var t=!!(e.f&2);if(Nn?.add(e),W!==null&&!Ln&&!(G!==null&&G.f&16384)&&(Bn===null||!Bn.has(e))){var r=W.deps;if(W.f&2097152)e.rv<Kn&&(e.rv=Kn,K===null&&r!==null&&r[Hn]===e?Hn++:K===null?K=[e]:K.push(e));else{W.deps??=[],n.call(W.deps,e)||W.deps.push(e);var i=e.reactions;i===null?e.reactions=[W]:n.call(i,W)||i.push(W)}}if(Fn&&zt.has(e))return zt.get(e);if(t){var a=e;if(Fn){var o=a.v;return(!(a.f&1024)&&a.reactions!==null||ar(a))&&(o=gt(a)),zt.set(a,o),o}var s=!(a.f&512)&&!Ln&&W!==null&&(Pn||!!(W.f&512)),c=(a.f&b)===0;Xn(a)&&(s&&(a.f|=512),_t(a)),s&&!c&&(yt(a),ir(a))}if(St?.has(e))return St.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)&&(yt(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(zt.has(t)||t.f&2&&ar(t))return!0;return!1}function J(e){var t=Ln;try{return Ln=!0,e()}finally{Ln=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 it(()=>n?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?qe(()=>{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)&&mn(()=>{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=W,f=G;Rn(null),zn(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,Rn(d),zn(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=on(`template`);return t.innerHTML=vr(e.replaceAll(`<!>`,`<!---->`)),t.content}function br(e,t){var n=G;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=en(i)));var t=r||Xt?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=en(t),s=t.lastChild;br(o,s)}else br(t,t);return t}}function xr(e=``){if(!O){var t=R(e+``);return br(t,t),t}var n=k;return n.nodeType===3?sn(n):(n.before(n=R()),A(n)),br(n,n),n}function Sr(){if(O)return br(k,null),k;var e=document.createDocumentFragment(),t=document.createComment(``),n=R();return e.append(t,n),br(t,n),e}function Z(e,t){if(O){var n=G;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=k),xe();return}e!==null&&e.before(t)}function Cr(e){let t=0,n=Vt(0),r;return()=>{pn()&&(q(n),bn(()=>(t===0&&(r=J(()=>e(()=>Gt(n)))),t+=1,()=>{qe(()=>{--t,t===0&&(r?.(),r=void 0,Gt(n))})})))}}var wr=S|C;function Tr(e,t,n,r){new Er(e,t,n,r)}var Er=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=Cr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=G;t.b=this,t.f|=128,n(e)},this.parent=G.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=xn(()=>{if(O){let e=this.#t;xe();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()},wr),O&&(this.#e=k)}#g(){try{this.#a=Sn(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);qe(r),t&&(this.#s=Sn(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){ye();return}t=!0,n&&ze(),this.#s!==null&&On(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){ln(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=Sn(()=>e(this.#e)),qe(()=>{var e=this.#c=document.createDocumentFragment(),t=R(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return Sn(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){ln(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(P);return}this.#u===0&&(this.#e.before(e),this.#c=null,On(this.#o,()=>{this.#o=null}),this.#x(P))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=Sn(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Mn(this.#a,e);let t=this.#n.pending;this.#o=Sn(()=>t(this.#e))}else this.#x(P)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){Qe(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=G,n=W,r=M;zn(this.#i),Rn(this.#i),Be(this.#i.ctx);try{return At.ensure(),e()}finally{zn(t),Rn(n),Be(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&&On(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,qe(()=>{this.#d=!1,this.#m&&Ut(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),q(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;P?.is_fork?(this.#a&&P.skip_effect(this.#a),this.#o&&P.skip_effect(this.#o),this.#s&&P.skip_effect(this.#s),P.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(U(this.#a),null),this.#o&&=(U(this.#o),null),this.#s&&=(U(this.#s),null),O&&(A(this.#t),Se(),A(Ce()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return Sn(()=>{var r=G;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return ln(e,this.#i.parent),null}}))};qe(()=>{var t;try{t=this.transform_error(e)}catch(e){ln(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>ln(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 Dr(e,t){return kr(e,t)}var Or=new Map;function kr(e,{target:t,anchor:n,props:i={},events:a,context:o,intro:s=!0,transformError:c}){$t();var l=void 0,u=_n(()=>{var s=n??t.appendChild(R());Tr(s,{pending:()=>{}},t=>{Ve({});var n=M;if(o&&(n.c=o),a&&(i.$$events=a),O&&br(t,null),l=e(t,i)||Ue(),O&&(G.nodes.end=k,k===null||k.nodeType!==8||k.data!==`]`))throw _e(),me;He()},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=Or.get(e);a===void 0&&(a=new Map,Or.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=Or.get(n),i=r.get(e);--i==0?(n.removeEventListener(e,gr),r.delete(e),r.size===0&&Or.delete(n)):r.set(e,i)}ur.delete(d),s!==n&&s.parentNode?.removeChild(s)}});return Ar.set(l,u),l}var Ar=new WeakMap,jr=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)An(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(An(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&&(U(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();Mn(r,t),t.append(R()),this.#n.set(e,{effect:r,fragment:t})}else U(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),On(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)||(U(n.effect),this.#n.delete(e))};ensure(e,t){var n=P,r=an();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=R();i.append(a),this.#n.set(e,{effect:Sn(()=>t(a)),fragment:i})}else this.#t.set(e,Sn(()=>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,xe());var i=new jr(e),a=n?S:0;function o(e,t){if(O){var n=we(r);if(e!==parseInt(n.substring(1))){var a=Ce();A(a),i.anchor=a,be(!1),i.ensure(e,t),be(!0);return}}i.ensure(e,t)}xn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}function Mr(e,t,n){for(var i=[],a=t.length,o,s=t.length,c=0;c<a;c++){let n=t[c];On(n,()=>{if(o){if(o.pending.delete(n),o.done.add(n),o.pending.size===0){var t=e.outrogroups;Nr(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;rn(d),d.append(u),e.items.clear()}Nr(e,t,!l)}else o={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(o)}function Nr(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,Mn(a,document.createDocumentFragment())):U(t[i],n)}}var Pr;function Fr(t,n,i,a,o,s=null){var c=t,l=new Map;if(n&4){var u=t;c=O?A(en(u)):u.appendChild(R())}O&&xe();var d=null,f=mt(()=>{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,Lr(v,p,c,n,a),d!==null&&(p.length===0?d.f&33554432?(d.f^=T,zr(d,null,c)):An(d):On(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:xn(()=>{p=q(f);var e=p.length;let t=!1;O&&we(c)===`[!`!=(e===0)&&(c=Ce(),A(c),be(!1),t=!0);for(var r=new Set,u=P,v=an(),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&&Ut(S.v,b),S.i&&Ut(S.i,y),v&&u.unskip_effect(S.e)):(S=Rr(l,h?c:Pr??=R(),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=Sn(()=>s(c)):(d=Sn(()=>s(Pr??=R())),d.f|=T)),e>r.size&&Ae(``,``,``),O&&e>0&&A(Ce()),!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 Ir(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Lr(e,t,n,i,a){var o=!!(i&8),s=t.length,c=e.items,l=Ir(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&&(An(_),o&&(_.nodes?.a?.unfix(),(f??=new Set).delete(_))),_.f&33554432){if(_.f^=T,_===l)zr(_,null,n);else{var y=d?d.next:l;_===e.effect.last&&(e.effect.last=_.prev),_.prev&&(_.prev.next=_.next),_.next&&(_.next.prev=_.prev),Br(e,d,_),Br(e,_,y),zr(_,y,n),d=_,p=[],m=[],l=Ir(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)zr(p[x],b,n);for(x=0;x<m.length;x+=1)u.delete(m[x]);Br(e,S.prev,C.next),Br(e,d,S),Br(e,C,b),l=b,d=C,--v,p=[],m=[]}else u.delete(_),zr(_,l,n),Br(e,_.prev,_.next),Br(e,_,d===null?e.effect.first:d.next),Br(e,d,_),d=_;continue}for(p=[],m=[];l!==null&&l!==_;)(u??=new Set).add(l),m.push(l),l=Ir(l.next);if(l===null)continue}_.f&33554432||p.push(_),d=_,l=Ir(_.next)}if(e.outrogroups!==null){for(let t of e.outrogroups)t.pending.size===0&&(Nr(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=Ir(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()}Mr(e,w,ee)}}o&&qe(()=>{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Rr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):Ht(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:Sn(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function zr(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=tn(r);if(a.before(r),r===i)return;r=o}}function Br(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}var Vr=[...`
2
+ \r\f\xA0\v`];function Hr(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||Vr.includes(r[o-1]))&&(s===r.length||Vr.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function Ur(e,t,n,r,i,a){var o=e[ce];if(O||o!==n||o===void 0){var s=Hr(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 Wr(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function Gr(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=Yr(c);Wr(c,i?a.includes(l):Jt(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 Kr(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(Yr(i));return}for(i of t.options)if(Jt(Yr(i),n)){i.selected=!0;return}(!r||n!==void 0)&&(t.selectedIndex=-1)}function qr(e){var t=new MutationObserver(t=>{t.every(Xr)||(`__defaultValue`in e&&Gr(e,!1),`__value`in e&&Kr(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),mn(()=>{t.disconnect()})}function Jr(e,t,n=t){var r=new WeakSet,i=!0;at(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),Yr);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&Yr(o)}n(a),e.__value=a,P!==null&&r.add(P)}),vn(()=>{var a=t();if(e===document.activeElement){var o=P;if(r.has(o))return}if(Kr(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=Yr(s),n(a))}e.__value=a,i=!1})}function Yr(e){return`__value`in e?e.__value:e.value}function Xr(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 Zr=Symbol(`is custom element`),Qr=Symbol(`is html`),$r=pe?`link`:`LINK`;function ei(e){if(O){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;ni(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;ni(e,`checked`,null),e.checked=r}}};e[de]=n,qe(n),rt()}}function ti(e,t){var n=ri(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function ni(e,t,n,r){var i=ri(e);O&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===$r)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[oe]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&ai(e).has(t)?e[t]=n:e.setAttribute(t,n))}function ri(e){return e[se]??={[Zr]:e.nodeName.includes(`-`),[Qr]:e.namespaceURI===he}}var ii=new Map;function ai(e){var t=e.getAttribute(`is`)||e.nodeName,n=ii.get(t);if(n)return n;ii.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 oi(e,t,n=t){var r=new WeakSet;at(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=ci(e)?li(a):a,n(a),P!==null&&r.add(P),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(ci(e)?li(e.value):e.value),P!==null&&r.add(P)),bn(()=>{var n=t();if(e===document.activeElement){var i=P;if(r.has(i))return}ci(e)&&n===li(e.value)||e.type===`date`&&!n&&!e.value||n!==e.value&&(e.value=n??``)})}function si(e,t,n=t){at(e,`change`,t=>{n(t?e.defaultChecked:e.checked)}),(O&&e.defaultChecked!==e.checked||J(t)==null)&&n(e.checked),bn(()=>{e.checked=!!t()})}function ci(e){var t=e.type;return t===`number`||t===`range`}function li(e){return e===``?null:+e}function ui(e,t,n,r){var i=!0,o=!!(n&8),s=!!(n&16),c=r,l=!0,u=void 0,d=()=>s&&i?(u??=ut(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]=et(()=>e[t]):m=e[t],m===void 0&&r!==void 0&&(m=d(),f&&(i&&Fe(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?ut:mt)(()=>(v=!1,g()));o&&q(y);var b=G;return(function(e,t){if(arguments.length>0){let n=t?q(y):i&&o?L(e):e;return I(y,n),v=!0,c!==void 0&&(c=n),e}return Fn&&v||b.f&16384?y.v:q(y)})}function di(e){M===null&&Oe(`onMount`),hn(()=>{let t=J(e);if(typeof t==`function`)return t})}typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var fi=X(`<button>Edit this disc</button>`),pi=X(`<button>Add this disc</button>`),mi=X(`<div><div><h4> </h4> <p> </p></div> <!></div>`);function hi(e,t){Ve(t,!0);let n=F(null);hn(()=>{let e=new EventSource(`/api/v1/current-tag/events`);return e.onmessage=e=>{I(n,JSON.parse(e.data),!0)},()=>e.close()});var r=Sr(),i=nn(r),a=e=>{var r=mi();let i;var a=z(r),o=z(a),s=B(o,!0),c=B(V(o,2));j(a);var l=V(a,2),u=e=>{var r=fi();Y(`click`,r,()=>t.onEditDisc?.(q(n).tag_id)),Z(e,r)},d=e=>{var r=pi();Y(`click`,r,()=>t.onAddDisc?.(q(n).tag_id)),Z(e,r)};$(l,e=>{q(n).known_in_library?e(u):e(d,-1)}),j(r),H(()=>{i=Ur(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),He()}pr([`click`]);var gi=`/api/v1`,_i=class extends Error{constructor(e,t){super(`API error ${e}`),this.status=e,this.body=t}};async function vi(e,t={}){let n=await fetch(`${gi}${e}`,{headers:{"Content-Type":`application/json`,...t.headers},...t});if(!n.ok)throw new _i(n.status,await n.json().catch(()=>null));return n.status===204?null:n.json()}function yi(e){return vi(e)}function bi(e,t){return vi(e,{method:`POST`,body:JSON.stringify(t)})}function xi(e,t){return vi(e,{method:`PUT`,body:JSON.stringify(t)})}function Si(e,t){return vi(e,{method:`PATCH`,body:JSON.stringify(t)})}function Ci(e){return vi(e,{method:`DELETE`})}var wi=X(`<p class="error"> </p>`),Ti=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 Ei(e,t){Ve(t,!0);let n=ui(t,`tagId`,3,``),r=ui(t,`disc`,3,null),i=F(L(J(()=>n()))),a=F(L(J(()=>r()?.uri??``))),o=F(L(J(()=>r()?.metadata.artist??``))),s=F(L(J(()=>r()?.metadata.album??``))),c=F(L(J(()=>r()?.metadata.track??``))),l=F(L(J(()=>r()?.metadata.playlist??``))),u=F(L(J(()=>r()?.option.shuffle??!1))),d=F(null),f=F(!1);async function p(e){e.preventDefault(),I(f,!0),I(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 bi(`/discs/${q(i)}`,n):await Si(`/discs/${q(i)}`,n),t.onSaved()}catch(e){I(d,e.body?.detail??e.message,!0)}finally{I(f,!1)}}var m=Ti(),h=z(m),g=V(z(h));ei(g),j(h);var _=V(h,2),v=V(z(_));ei(v),j(_);var y=V(_,2),b=V(z(y));ei(b),j(y);var x=V(y,2),S=V(z(x));ei(S),j(x);var C=V(x,2),w=V(z(C));ei(w),j(C);var T=V(C,2),E=V(z(T));ei(E),j(T);var ee=V(T,2),te=z(ee);ei(te),Se(),j(ee);var ne=V(ee,2),re=e=>{var t=wi(),n=B(t,!0);H(()=>Q(n,q(d))),Z(e,t)};$(ne,e=>{q(d)&&e(re)});var ie=V(ne,2),ae=z(ie),oe=B(ae,!0),se=V(ae,2);j(ie),j(m),H(()=>{g.disabled=t.mode===`edit`,ae.disabled=q(f),Q(oe,q(f)?`Saving…`:`Save`)}),fr(`submit`,m,p),oi(g,()=>q(i),e=>I(i,e)),oi(v,()=>q(a),e=>I(a,e)),oi(b,()=>q(o),e=>I(o,e)),oi(S,()=>q(s),e=>I(s,e)),oi(w,()=>q(c),e=>I(c,e)),oi(E,()=>q(l),e=>I(l,e)),si(te,()=>q(u),e=>I(u,e)),Y(`click`,se,function(...e){t.onCancel?.apply(this,e)}),Z(e,m),He()}pr([`click`]);var Di=X(`<button>Add disc</button>`),Oi=X(`<p>Loading…</p>`),ki=X(`<p class="error"> </p>`),Ai=X(`<p>No disc found</p>`),ji=X(`<span class="tag-spin" aria-hidden="true">💿</span>`),Mi=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>`),Ni=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),Pi=X(`<div class="page-header"><h2>Library</h2> <!></div> <!>`,1);function Fi(e,t){Ve(t,!0);let n=ui(t,`intent`,3,null),r=F(L({})),i=F(!0),a=F(null),o=F(null),s=F(null),c=F(null),l=F(null);hn(()=>{let e=new EventSource(`/api/v1/current-tag/events`);return e.onmessage=e=>{let t=JSON.parse(e.data);I(s,t?.known_in_library?t.tag_id:null,!0)},()=>e.close()});async function u(){I(i,!0);try{I(r,await yi(`/discs`),!0),I(a,null)}catch(e){I(a,e.message,!0)}finally{I(i,!1)}}di(u),hn(()=>{!n()||q(i)||(n().type===`edit`?f(n().tagId):d(n().tagId),t.onIntentConsumed?.())});function d(e=``){I(o,{type:`create`,tagId:e},!0)}function f(e){I(o,{type:`edit`,tagId:e,disc:q(r)[e]},!0)}function p(){I(o,null)}async function h(){I(o,null),await u()}async function g(e,t){await navigator.clipboard.writeText(t),I(c,e,!0),setTimeout(()=>{q(c)===e&&I(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 Ci(`/discs/${e}`),I(l,null),await u()}catch(e){I(l,e.body?.detail??e.message,!0)}}var b=Pi(),x=nn(b),S=V(z(x),2),C=e=>{var t=Di();Y(`click`,t,()=>d()),Z(e,t)};$(S,e=>{q(o)||e(C)}),j(x);var w=V(x,2),T=e=>{Ei(e,{mode:`create`,get tagId(){return q(o).tagId},onSaved:h,onCancel:p})},E=e=>{Ei(e,{mode:`edit`,get tagId(){return q(o).tagId},get disc(){return q(o).disc},onSaved:h,onCancel:p})},ee=e=>{var t=Sr(),n=nn(t),o=e=>{Z(e,Oi())},u=e=>{var t=ki(),n=B(t,!0);H(()=>Q(n,q(a))),Z(e,t)},d=e=>{Z(e,Ai())},p=pt(()=>Object.keys(q(r)).length===0),h=e=>{var t=Ni(),n=nn(t),i=e=>{var t=ki(),n=B(t,!0);H(()=>Q(n,q(l))),Z(e,t)};$(n,e=>{q(l)&&e(i)});var a=V(n,2),o=V(z(a),2);Fr(o,21,()=>Object.entries(q(r)),([e,t])=>e,(e,t)=>{var n=pt(()=>m(q(t),2));let r=()=>q(n)[0],i=()=>q(n)[1];var a=Mi(),o=z(a),l=z(o),u=e=>{Z(e,ji())};$(l,e=>{r()===q(s)&&e(u)}),j(o);var d=V(o),p=B(d,!0),h=V(d),b=z(h),x=z(b),S=B(x,!0),C=V(x,2),w=B(C,!0);j(b),j(h);var T=V(h),E=z(T),ee=B(E,!0),te=B(V(E,2),!0);j(T);var ne=V(T),re=B(ne,!0),ie=V(ne),ae=z(ie);let oe;j(ie);var se=V(ie),ce=z(se),le=V(ce,2);j(se),j(a),H((e,t)=>{Q(p,r()),ni(h,`title`,i().uri),Q(S,i().uri),ni(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=Ur(ae,1,`shuffle-icon`,null,oe,{active:i().option.shuffle}),ni(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)}),j(o),j(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),He()}pr([`click`]);var Ii=X(`<option> </option>`),Li=X(`<select></select>`),Ri=X(`<textarea rows="10" placeholder="Enter a JSON object. Leave blank to persist null."></textarea>`),zi=X(`<input/>`),Bi=X(`<p class="error"> </p>`),Vi=X(`<button type="button">Reset</button>`),Hi=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 Ui(e,t){Ve(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=F(L(J(()=>n(t.setting)))),i=F(null),a=F(!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(),I(i,null),I(a,!0);try{let e=s();await Si(`/settings`,o(t.setting.path,e)),t.onSaved()}catch(e){I(i,e.body?.detail??e.message,!0)}finally{I(a,!1)}}async function l(){I(i,null),I(a,!0);try{await bi(`/settings/reset`,{path:t.setting.path}),t.onSaved()}catch(e){I(i,e.body?.detail??e.message,!0)}finally{I(a,!1)}}var u=Hi(),d=z(u),f=B(d),p=V(d,2),m=B(p,!0),h=V(p,2),g=B(h,!0),_=V(h,2),v=e=>{var n=Li();Fr(n,21,()=>t.setting.choices,e=>e.value,(e,t)=>{var n=Ii(),r=B(n,!0),i={};H(()=>{Q(r,q(t).label),i!==(i=q(t).value)&&(n.value=(n.__value=i)??``)}),Z(e,n)}),j(n),qr(n),Jr(n,()=>q(r),e=>I(r,e)),Z(e,n)},y=e=>{var t=Ri();tt(t),oi(t,()=>q(r),e=>I(r,e)),Z(e,t)},b=e=>{var n=zi();ei(n),H(()=>{ni(n,`type`,t.setting.field_type===`integer`||t.setting.field_type===`number`?`number`:`text`),ni(n,`step`,t.setting.field_type===`number`?`any`:void 0)}),oi(n,()=>q(r),e=>I(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=V(_,2),S=e=>{var t=Bi(),n=B(t,!0);H(()=>Q(n,q(i))),Z(e,t)};$(x,e=>{q(i)&&e(S)});var C=V(x,2),w=z(C),T=B(w,!0),E=V(w,2),ee=e=>{var t=Vi();H(()=>t.disabled=q(a)),Y(`click`,t,l),Z(e,t)};$(E,e=>{t.setting.is_persisted&&e(ee)});var te=V(E,2);j(C),j(u),H(()=>{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),He()}pr([`click`]);var Wi=X(`<p>Loading…</p>`),Gi=X(`<p class="error"> </p>`),Ki=X(`<span class="badge badge-configured">Configured</span>`),qi=X(`<span class="badge badge-pinned">Pinned default</span>`),Ji=X(`<span class="badge badge-restart">Restart required</span>`),Yi=X(`<span class="badge badge-advanced">Advanced</span>`),Xi=X(`<span class="path">—</span>`),Zi=X(`<button>Manage Speakers 🔊</button>`),Qi=X(`<button>Edit</button>`),$i=X(`<tr><td><strong> </strong> <!> <!> <!> <!> <div class="path"> </div></td><td> </td><td><!></td><td> </td><td> </td><td><!></td></tr>`),ea=X(`<h3> </h3> <table><thead><tr><th>Setting</th><th>Default</th><th>Persisted</th><th>Effective value</th><th>Source</th><th></th></tr></thead><tbody></tbody></table>`,1),ta=X(`<!> <!>`,1),na=X(`<h2>Settings</h2> <!>`,1);function ra(e,t){Ve(t,!0);let n=F(L([])),r=F(null),i=F(!0),a=F(null),o=F(null);async function s(){I(i,!0);try{let e=await yi(`/settings/displays`);I(n,e.settings,!0),I(r,e.effective_settings_error,!0),I(a,null)}catch(e){I(a,e.message,!0)}finally{I(i,!1)}}di(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=pt(()=>c(q(n))),d=pt(()=>q(n).find(e=>e.path===q(o))??null);function f(e){I(o,e,!0)}function p(){I(o,null)}async function m(){I(o,null),await s()}var h=na(),g=V(nn(h),2),_=e=>{Ui(e,{get setting(){return q(d)},onSaved:m,onCancel:p})},v=e=>{Z(e,Wi())},y=e=>{var t=Gi(),n=B(t,!0);H(()=>Q(n,q(a))),Z(e,t)},b=e=>{var n=ta(),i=nn(n),a=e=>{var t=Gi(),n=B(t);H(()=>Q(n,`${q(r)??``} Persisted overrides are still shown below.`)),Z(e,t)};$(i,e=>{q(r)&&e(a)}),Fr(V(i,2),17,()=>q(u),e=>e.section,(e,n)=>{var r=ea(),i=nn(r),a=B(i,!0),o=V(i,2),s=V(z(o));Fr(s,21,()=>q(n).entries,e=>e.path,(e,n)=>{var r=$i(),i=z(r),a=z(i),o=B(a,!0),s=V(a,2),c=e=>{Z(e,Ki())};$(s,e=>{q(n).is_persisted&&e(c)});var u=V(s,2),d=e=>{Z(e,qi())};$(u,e=>{q(n).is_pinned_default&&e(d)});var p=V(u,2),m=e=>{Z(e,Ji())};$(p,e=>{q(n).requires_restart&&e(m)});var h=V(p,2),g=e=>{Z(e,Yi())};$(h,e=>{q(n).advanced&&e(g)});var _=B(V(h,2),!0);j(i);var v=V(i),y=B(v,!0),b=V(v),x=z(b),S=e=>{var t=xr();H(e=>Q(t,e),[()=>l(q(n).persisted_value)]),Z(e,t)},C=e=>{Z(e,Xi())};$(x,e=>{q(n).is_persisted?e(S):e(C,-1)}),j(b);var w=V(b),T=B(w,!0),E=V(w),ee=B(E,!0),te=V(E),ne=z(te),re=e=>{var n=Zi();Y(`click`,n,()=>t.onManageSpeakers?.()),Z(e,n)},ie=e=>{var t=Qi();Y(`click`,t,()=>f(q(n).path)),Z(e,t)};$(ne,e=>{q(n).path===`jukebox.player.sonos.selected_group`?e(re):e(ie,-1)}),j(te),j(r),H((e,t)=>{Q(o,q(n).label),Q(_,q(n).path),Q(y,e),Q(T,t),Q(ee,q(n).provenance)},[()=>l(q(n).default_value),()=>l(q(n).effective_value)]),Z(e,r)}),j(s),j(o),H(()=>Q(a,q(n).label)),Z(e,r)}),Z(e,n)};$(g,e=>{q(d)?e(_):q(i)?e(v,1):q(a)?e(y,2):e(b,-1)}),Z(e,h),He()}pr([`click`]);var ia=X(`<label class="checkbox"><input type="checkbox"/> </label>`),aa=X(`<option> </option>`),oa=X(`<p class="error"> </p>`),sa=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 ca(e,t){Ve(t,!0);function n(){return t.selectedGroup?t.selectedGroup.members.map(e=>e.uid):t.speakers[0]?[t.speakers[0].uid]:[]}let r=F(L(J(()=>new Set(n())))),i=F(L(J(()=>t.selectedGroup?.coordinator_uid??n()[0]??``))),a=F(null),o=F(!1);function s(e){let t=new Set(q(r));t.has(e)?t.delete(e):t.add(e),I(r,t,!0)}async function c(e){e.preventDefault(),I(a,null),I(o,!0);try{await xi(`/sonos/selection`,{uids:[...q(r)],coordinator_uid:q(i)}),t.onSaved()}catch(e){I(a,e.body?.detail??e.message,!0)}finally{I(o,!1)}}var l=sa(),u=V(z(l),6);Fr(V(z(u),2),17,()=>t.speakers,e=>e.uid,(e,t)=>{var n=ia(),i=z(n);ei(i);var a=V(i);j(n),H(e=>{ti(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)}),j(u);var d=V(u,2),f=V(z(d));Fr(f,21,()=>t.speakers,e=>e.uid,(e,t)=>{var n=aa(),r=B(n),i={};H(()=>{Q(r,`${q(t).name??``} (${q(t).host??``})`),i!==(i=q(t).uid)&&(n.value=(n.__value=i)??``)}),Z(e,n)}),j(f),qr(f),j(d);var p=V(d,2),m=e=>{var t=oa(),n=B(t,!0);H(()=>Q(n,q(a))),Z(e,t)};$(p,e=>{q(a)&&e(m)});var h=V(p,2),g=z(h),_=B(g,!0),v=V(g,2);j(h),j(l),H(()=>{g.disabled=q(o),Q(_,q(o)?`Saving…`:`Save`)}),fr(`submit`,l,c),Jr(f,()=>q(i),e=>I(i,e)),Y(`click`,v,function(...e){t.onCancel?.apply(this,e)}),Z(e,l),He()}pr([`change`,`click`]);var la=X(`<p>Loading…</p>`),ua=X(`<p class="error"> </p>`),da=X(`<p>No Sonos speaker selection is currently saved.</p>`),fa=X(`<p> </p> <p> </p> <p> </p>`,1),pa=X(`<button class="btn-danger">Clear saved selection</button>`),ma=X(`<p>No visible Sonos speakers found.</p>`),ha=X(`<tr><td> </td><td> </td><td> </td><td class="center"> </td></tr>`),ga=X(`<table><thead><tr><th>Name</th><th>Host</th><th>Household</th><th>Selection</th></tr></thead><tbody></tbody></table>`),_a=X(`<h3>Discovered speakers</h3> <!>`,1),va=X(`<!> <div class="panel"><h3>Saved selection</h3> <!></div> <div class="row-actions"><button>Edit selection</button> <!></div> <!>`,1),ya=X(`<h2>Sonos</h2> <!>`,1);function ba(e,t){Ve(t,!0);let n={available:`Available`,partial:`Partially available`,unavailable:`Unavailable`,not_selected:`Not selected`},r=F(null),i=F(L([])),a=F(!0),o=F(null),s=F(null),c=F(!1);async function l(){I(a,!0),I(o,null),I(s,null);try{I(r,await yi(`/sonos/selection`),!0),I(i,await yi(`/sonos/speakers`),!0)}catch(e){e instanceof _i&&e.status===502?I(o,e.body?.detail??`Sonos discovery unavailable.`,!0):I(s,e.message,!0)}finally{I(a,!1)}}di(l);let u=pt(()=>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(){I(c,!0)}function m(){I(c,!1)}async function h(){I(c,!1),await l()}async function g(){await bi(`/settings/reset`,{path:`jukebox.player.sonos.selected_group`}),await l()}var _=ya(),v=V(nn(_),2),y=e=>{{let t=pt(()=>q(r)?.selected_group??null);ca(e,{get speakers(){return q(i)},get selectedGroup(){return q(t)},onSaved:h,onCancel:m})}},b=e=>{Z(e,la())},x=e=>{var t=ua(),n=B(t,!0);H(()=>Q(n,q(s))),Z(e,t)},S=e=>{var t=va(),a=nn(t),s=e=>{var t=ua(),n=B(t);H(()=>Q(n,`Sonos discovery unavailable: ${q(o)??``}`)),Z(e,t)};$(a,e=>{q(o)&&e(s)});var c=V(a,2),l=V(z(c),2),u=e=>{Z(e,da())},m=e=>{var t=fa(),i=nn(t),a=B(i),o=V(i,2),s=B(o),c=B(V(o,2));H((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)}),j(c);var h=V(c,2),_=z(h),v=V(_,2),y=e=>{var t=pa();Y(`click`,t,g),Z(e,t)};$(v,e=>{q(r)?.selected_group&&e(y)}),j(h);var b=V(h,2),x=e=>{var t=_a(),n=V(nn(t),2),r=e=>{Z(e,ma())},a=e=>{var t=ga(),n=V(z(t));Fr(n,21,()=>q(i),e=>e.uid,(e,t)=>{var n=ha(),r=z(n),i=B(r,!0),a=V(r),o=B(a,!0),s=V(a),c=B(s,!0),l=B(V(s),!0);j(n),H(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)}),j(n),j(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,_),He()}pr([`click`]);var xa=X(`<button> </button>`),Sa=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 Ca(e){let t=[`library`,`settings`,`sonos`],n=F(`library`),r=F(null);function i(e){I(r,e,!0),I(n,`library`)}function a(){I(r,null)}function o(){I(n,`sonos`)}var s=Sa(),c=nn(s);hi(c,{onEditDisc:e=>i({type:`edit`,tagId:e}),onAddDisc:e=>i({type:`create`,tagId:e})});var l=V(c,2);Fr(l,20,()=>t,e=>e,(e,t)=>{var r=xa();let i;var a=B(r,!0);H(()=>{i=Ur(r,1,``,null,i,{active:q(n)===t}),Q(a,t)}),Y(`click`,r,()=>I(n,t,!0)),Z(e,r)}),j(l);var u=V(l,2),d=z(u),f=e=>{Fi(e,{get intent(){return q(r)},onIntentConsumed:a})},p=e=>{ra(e,{onManageSpeakers:o})},m=e=>{ba(e,{})};$(d,e=>{q(n)===`library`?e(f):q(n)===`settings`?e(p,1):q(n)===`sonos`&&e(m,2)}),j(u),Se(2),Z(e,s)}pr([`click`]),Dr(Ca,{target:document.getElementById(`app`)});
@@ -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-S2QaCKBl.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/ui/assets/index-CYMkUb9q.css">
10
+ </head>
11
+ <body>
12
+ <div id="app"></div>
13
+ </body>
14
+ </html>
@@ -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
- execute_sonos_command(
112
- command=command,
113
- sonos_service=services.sonos,
114
- settings_service=services.settings,
115
- household_prompt_fn=_prompt_for_sonos_household_selection,
116
- speaker_prompt_fn=_prompt_for_sonos_speaker_selection,
117
- coordinator_prompt_fn=_prompt_for_sonos_group_coordinator,
118
- status_fn=_emit_cli_status,
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
- def _emit_cli_status(message: str) -> None:
291
+ @contextmanager
292
+ def _sonos_command_status():
288
293
  if not sys.stderr.isatty():
294
+ yield None, None
289
295
  return
290
- typer.echo(message, err=True)
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,
@@ -7,7 +7,7 @@ from jukebox.settings.entities import (
7
7
  SelectedSonosSpeakerSettings,
8
8
  )
9
9
 
10
- from .discovery import DiscoveredSonosSpeaker
10
+ from .discovery import DiscoveredSonosSpeaker, SonosDiscoveryError
11
11
  from .service import SonosService
12
12
 
13
13
 
@@ -99,7 +99,18 @@ class GetSonosSelectionStatus:
99
99
  availability=SonosSelectionAvailability(status="not_selected"),
100
100
  )
101
101
 
102
- inspection = self.sonos_service.inspect_selected_group(selected_group)
102
+ try:
103
+ inspection = self.sonos_service.inspect_selected_group(selected_group)
104
+ except SonosDiscoveryError:
105
+ members = [
106
+ SonosSelectionMemberAvailability(uid=saved_member.uid, status="unavailable")
107
+ for saved_member in selected_group.members
108
+ ]
109
+ return SonosSelectionStatus(
110
+ selected_group=selected_group,
111
+ availability=SonosSelectionAvailability(status="unavailable", members=members),
112
+ )
113
+
103
114
  available_speakers = {speaker.uid: speaker for speaker in inspection.resolved_members}
104
115
  members = []
105
116
 
@@ -1,14 +1,14 @@
1
1
  [project]
2
2
  name = "gukebox"
3
- version = "1.4.3"
3
+ version = "1.5.0.dev2"
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.4",
8
+ "pydantic==2.13.5",
9
9
  "questionary==2.1.1",
10
- "soco==0.31.1",
11
- "typer==0.27.0",
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.140.13",
40
- "uvicorn==0.51.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.0",
63
- "ty==0.0.64",
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.4.3"
3
+ version = "1.5.0.dev2"
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.4",
10
+ "pydantic==2.13.5",
11
11
  "questionary==2.1.1",
12
- "soco==0.31.1",
13
- "typer==0.27.0",
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.140.13",
32
- "uvicorn==0.51.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.0",
51
- "ty==0.0.64",
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes