gukebox 1.0.0.dev7__tar.gz → 1.0.0.dev9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/PKG-INFO +32 -58
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/README.md +30 -56
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/api/current_tag_router.py +9 -6
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/api/discs_router.py +5 -2
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/api_controller.py +3 -3
- gukebox-1.0.0.dev9/discstore/adapters/inbound/ui_controller.py +511 -0
- gukebox-1.0.0.dev9/discstore/adapters/inbound/ui_pages/library.py +375 -0
- gukebox-1.0.0.dev9/discstore/adapters/inbound/ui_pages/settings.py +523 -0
- gukebox-1.0.0.dev9/discstore/adapters/inbound/ui_pages/sonos.py +502 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/commands.py +1 -32
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/use_cases/edit_disc.py +2 -2
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/adapters/inbound/config.py +5 -20
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/admin/app.py +0 -6
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/admin/cli_presentation.py +0 -40
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/admin/command_handlers.py +0 -11
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/admin/commands.py +1 -6
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/admin/di_container.py +2 -5
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/app.py +1 -1
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/entities.py +1 -6
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/resolve.py +6 -16
- gukebox-1.0.0.dev9/jukebox/shared/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/shared/config_utils.py +0 -13
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/sonos/selection.py +1 -6
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/pyproject.toml +2 -3
- gukebox-1.0.0.dev7/discstore/adapters/inbound/config.py +0 -250
- gukebox-1.0.0.dev7/discstore/adapters/inbound/ui_controller.py +0 -1084
- gukebox-1.0.0.dev7/discstore/app.py +0 -105
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/LICENSE +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/api/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/api/models.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/api/settings_router.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/cli_controller.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/cli_display.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/interactive_cli_controller.py +0 -0
- {gukebox-1.0.0.dev7/discstore/adapters/outbound → gukebox-1.0.0.dev9/discstore/adapters/inbound/ui_pages}/__init__.py +0 -0
- {gukebox-1.0.0.dev7/discstore/domain → gukebox-1.0.0.dev9/discstore/adapters/outbound}/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/outbound/json_library_adapter.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/outbound/text_current_tag_adapter.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/command_handlers.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/di_container.py +0 -0
- {gukebox-1.0.0.dev7/jukebox → gukebox-1.0.0.dev9/discstore/domain}/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/entities/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/entities/current_tag_status.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/repositories/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/use_cases/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/use_cases/add_disc.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/use_cases/get_current_tag_status.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/use_cases/get_disc.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/use_cases/list_discs.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/use_cases/remove_disc.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/use_cases/resolve_tag_id.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/domain/use_cases/search_discs.py +0 -0
- {gukebox-1.0.0.dev7/jukebox/adapters → gukebox-1.0.0.dev9/jukebox}/__init__.py +0 -0
- {gukebox-1.0.0.dev7/jukebox/adapters/inbound → gukebox-1.0.0.dev9/jukebox/adapters}/__init__.py +0 -0
- {gukebox-1.0.0.dev7/jukebox/adapters/outbound → gukebox-1.0.0.dev9/jukebox/adapters/inbound}/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/adapters/inbound/cli_controller.py +0 -0
- {gukebox-1.0.0.dev7/jukebox/adapters/outbound/players → gukebox-1.0.0.dev9/jukebox/adapters/outbound}/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/adapters/outbound/json_library_adapter.py +0 -0
- {gukebox-1.0.0.dev7/jukebox/adapters/outbound/readers → gukebox-1.0.0.dev9/jukebox/adapters/outbound/players}/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/adapters/outbound/players/dryrun_player_adapter.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/adapters/outbound/players/sonos_player_adapter.py +0 -0
- {gukebox-1.0.0.dev7/jukebox/domain → gukebox-1.0.0.dev9/jukebox/adapters/outbound/readers}/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/adapters/outbound/readers/dryrun_reader_adapter.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/adapters/outbound/readers/pn532_reader_adapter.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/adapters/outbound/sonos_discovery_adapter.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/adapters/outbound/text_current_tag_adapter.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/admin/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/admin/services.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/di_container.py +0 -0
- {gukebox-1.0.0.dev7/jukebox/shared → gukebox-1.0.0.dev9/jukebox/domain}/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/entities/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/entities/current_tag_action.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/entities/disc.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/entities/library.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/entities/playback_action.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/entities/playback_session.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/entities/tag_event.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/ports/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/ports/player_port.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/ports/reader_port.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/repositories/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/repositories/current_tag_repository.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/repositories/library_repository.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/use_cases/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/use_cases/determine_action.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/use_cases/determine_current_tag_action.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/domain/use_cases/handle_tag_event.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/definitions.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/dict_utils.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/errors.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/file_settings_repository.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/migration.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/repositories.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/runtime_resolver.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/runtime_validation.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/selected_sonos_group_repository.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/service_protocols.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/timing_validation.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/types.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/validation_rules.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/settings/view_utils.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/shared/dependency_messages.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/shared/logger.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/shared/timing.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/sonos/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/sonos/discovery.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/jukebox/sonos/service.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/pn532/__init__.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/pn532/pn532.py +0 -0
- {gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/pn532/spi.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: gukebox
|
|
3
|
-
Version: 1.0.0.
|
|
3
|
+
Version: 1.0.0.dev9
|
|
4
4
|
Summary: A Jukebox to play music on speakers using 'CD' with NFC tag
|
|
5
|
-
Keywords: jukebox,
|
|
5
|
+
Keywords: jukebox,music,nfc
|
|
6
6
|
Author: Gudsfile
|
|
7
7
|
License: MIT License
|
|
8
8
|
|
|
@@ -58,8 +58,9 @@ Description-Content-Type: text/markdown
|
|
|
58
58
|
|
|
59
59
|
# Jukebox \[gukebox\]
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
[
|
|
62
|
+
[](https://pypi.python.org/pypi/gukebox)
|
|
63
|
+
[](https://pypi.python.org/pypi/gukebox)
|
|
63
64
|
[](https://pypi.python.org/pypi/gukebox)
|
|
64
65
|
[](https://github.com/gudsfile/jukebox/actions)
|
|
65
66
|
[](https://github.com/astral-sh/uv)
|
|
@@ -70,7 +71,7 @@ Description-Content-Type: text/markdown
|
|
|
70
71
|
|
|
71
72
|
🚧 At the moment:
|
|
72
73
|
|
|
73
|
-
- NFC tags - CDs must be pre-populated in a JSON file (`
|
|
74
|
+
- NFC tags - CDs must be pre-populated in a JSON file (`jukebox-admin` included with `jukebox` may be of help to you)
|
|
74
75
|
- supports many music providers (Spotify, Apple Music, etc.), just add the URIs to the JSON file
|
|
75
76
|
- only works with Sonos speakers (there is a "dryrun" player for development), but code is designed to **add new ones**
|
|
76
77
|
- **as soon as** the NFC tag is removed, the music pauses, then resumes when the NFC tag is replaced
|
|
@@ -84,10 +85,9 @@ Description-Content-Type: text/markdown
|
|
|
84
85
|
|
|
85
86
|
- [Install](#install)
|
|
86
87
|
- [First steps](#first-steps)
|
|
87
|
-
- [Discstore](#manage-the-library-with-the-discstore)
|
|
88
88
|
- [Usage](#usage)
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
- [Readers](#readers)
|
|
90
|
+
- [Players](#players)
|
|
91
91
|
- [The library file](#the-library-file)
|
|
92
92
|
- [Developer setup](#developer-setup)
|
|
93
93
|
|
|
@@ -161,18 +161,18 @@ uv sync
|
|
|
161
161
|
|
|
162
162
|
## First steps
|
|
163
163
|
|
|
164
|
-
Initialize the library file with `
|
|
164
|
+
Initialize the library file with `jukebox-admin` or manually create it at `~/.config/jukebox/library.json`.
|
|
165
165
|
|
|
166
|
-
### Manage the library with the
|
|
166
|
+
### Manage the library with the Admin CLI
|
|
167
167
|
|
|
168
168
|
To associate an URI with an NFC tag:
|
|
169
169
|
|
|
170
170
|
```shell
|
|
171
|
-
|
|
171
|
+
jukebox-admin library add tag_id --uri /path/to/media.mp3
|
|
172
172
|
```
|
|
173
173
|
or to pull the `tag_id` currently on the reader:
|
|
174
174
|
```shell
|
|
175
|
-
|
|
175
|
+
jukebox-admin library add --from-current --uri /path/to/media.mp3
|
|
176
176
|
```
|
|
177
177
|
|
|
178
178
|
Other commands are available, use `--help` to see them.
|
|
@@ -204,8 +204,6 @@ uv run --extra api jukebox-admin api
|
|
|
204
204
|
uv run --extra ui jukebox-admin ui
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
-
`discstore settings ...`, `discstore api`, and `discstore ui` remain available as compatibility commands, but `jukebox-admin` is the preferred CLI for admin flows.
|
|
208
|
-
|
|
209
207
|
### Manage the library manually
|
|
210
208
|
|
|
211
209
|
Complete your `~/.config/jukebox/library.json` file with each tag id and the expected media URI.
|
|
@@ -216,16 +214,18 @@ Take a look at `library.example.json` and the [The library file](#the-library-fi
|
|
|
216
214
|
Start the jukebox with the `jukebox` command (show help message with `--help`)
|
|
217
215
|
|
|
218
216
|
```shell
|
|
219
|
-
jukebox
|
|
217
|
+
jukebox --player PLAYER --reader READER
|
|
220
218
|
```
|
|
221
219
|
|
|
222
220
|
🎉 With choosing the `sonos` player and `pn532` reader, by approaching a NFC tag stored in the `library.json` file, you should hear the associated music begins.
|
|
223
221
|
|
|
224
|
-
Optional Parameters
|
|
222
|
+
**Optional Parameters**
|
|
225
223
|
|
|
226
224
|
| Parameter | Description |
|
|
227
225
|
| --- | --- |
|
|
228
226
|
| `--help` | Show help message. |
|
|
227
|
+
| `--player PLAYER` | Player to use (`sonos`, `dryrun`). |
|
|
228
|
+
| `--reader READER` | Reader to use (`pn532`, `dryrun`). |
|
|
229
229
|
| `--library` | Path to the library file, default: `~/.config/jukebox/library.json`. |
|
|
230
230
|
| `--pause-delay SECONDS` | Grace period before pausing when the NFC tag is removed. Fractional values such as `0.5` or `0.2` are supported, with a minimum of `0.2` seconds to avoid pausing on brief missed reads. Default: 0.25 seconds. |
|
|
231
231
|
| `--pause-duration SECONDS` | Maximum duration of a pause before resetting the queue. Default: 900 seconds (15 minutes). |
|
|
@@ -234,40 +234,29 @@ Optional Parameters
|
|
|
234
234
|
|
|
235
235
|
### Readers
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
- tag_id: the full identifier of the tag, in the format required by the system
|
|
242
|
-
- duration_seconds: a non-negative number of seconds used to simulate how long the tag remains in place. Fractional values are allowed.
|
|
243
|
-
Complete example: `your:tag:uid 2.5`
|
|
237
|
+
| Name | Description |
|
|
238
|
+
| --- | --- |
|
|
239
|
+
| Dry Run (`dryrun`) | Simulates NFC tag reading via stdin. Input format: `tag_id` or `tag_id duration_seconds`. |
|
|
240
|
+
| Pn532 NFC (`pn532`) | Reads physical NFC tags. Works with a **PN532** reader and **NTAG2xx** tags. Requires the `pn532` extra and SPI enabled on the Raspberry Pi. |
|
|
244
241
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
This project works with an NFC reader like the **PN532** and NFC tags like the **NTAG2xx**.
|
|
248
|
-
It is configured according to the [Waveshare PN532 wiki](https://www.waveshare.com/wiki/PN532_NFC_HAT).
|
|
249
|
-
Don't forget to enable the SPI interface using the command `sudo raspi-config`, then go to: `Interface Options > SPI > Enable > Yes`.
|
|
242
|
+
> [!NOTE]
|
|
243
|
+
> See [docs/readers.md](docs/readers.md) for full setup, hardware requirements, and settings reference.
|
|
250
244
|
|
|
251
245
|
### Players
|
|
252
246
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
Play music through a Sonos speaker.
|
|
258
|
-
Three ways to select the speaker (mutually exclusive):
|
|
247
|
+
| Name | Description |
|
|
248
|
+
| --- | --- |
|
|
249
|
+
| Dry Run (`dryrun`) | Displays the events that a real speaker would have performed (`playing …`, `pause`, etc.). |
|
|
250
|
+
| Sonos (`sonos`) | [](https://github.com/SoCo/SoCo) Plays music through a Sonos speaker. Select by IP (`--sonos-host`), by name (`--sonos-name`), or let it auto-discover. |
|
|
259
251
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
| By IP | `--sonos-host 192.168.0.x` | `JUKEBOX_SONOS_HOST` | Connect directly, no discovery |
|
|
263
|
-
| By name | `--sonos-name "Living Room"` | `JUKEBOX_SONOS_NAME` | Discover, then filter by name (case-sensitive) |
|
|
264
|
-
| Auto | *(omit both)* | *(omit both)* | Discover, pick the first speaker alphabetically |
|
|
252
|
+
> [!NOTE]
|
|
253
|
+
> See [docs/players.md](docs/players.md) for the full configuration reference.
|
|
265
254
|
|
|
266
255
|
## The library file
|
|
267
256
|
|
|
268
257
|
The `library.json` file is a JSON file that contains the artists, albums and tags.
|
|
269
258
|
It is used by the `jukebox` command to find the corresponding metadata for each tag.
|
|
270
|
-
And the `
|
|
259
|
+
And the `jukebox-admin library` command help you to managed this file with a CLI, an interactive CLI, an API or an UI (see `jukebox-admin --help`).
|
|
271
260
|
|
|
272
261
|
By default, this file should be placed at `~/.config/jukebox/library.json`. But you can use another path by creating a `JUKEBOX_LIBRARY_PATH` environment variable or with the `--library` argument.
|
|
273
262
|
|
|
@@ -341,9 +330,7 @@ uv sync
|
|
|
341
330
|
|
|
342
331
|
Add `--all-extras` to install dependencies for all extras (`api` and `ui`).
|
|
343
332
|
|
|
344
|
-
If needed,
|
|
345
|
-
If neither is set, the jukebox will auto-discover a speaker on the network.
|
|
346
|
-
To do this you can use a `.env` file and `uv run --env-file .env <command to run>`.
|
|
333
|
+
If needed, you can use a `.env` file and `uv run --env-file .env <command to run>`.
|
|
347
334
|
A `.env.example` file is available, you can copy it and modify it to use it.
|
|
348
335
|
|
|
349
336
|
Create a `library.json` file and complete it with the desired NFC tags and CDs.
|
|
@@ -354,12 +341,7 @@ Take a look at `library.example.json` and the [The library file](#the-library-fi
|
|
|
354
341
|
Start the jukebox with `uv` and use `--help` to show help message
|
|
355
342
|
|
|
356
343
|
```shell
|
|
357
|
-
uv run jukebox PLAYER_TO_USE READER_TO_USE
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
Start the discstore `uv` and use `--help` to show help message
|
|
361
|
-
```shell
|
|
362
|
-
uv run discstore --help
|
|
344
|
+
uv run jukebox --player PLAYER_TO_USE --reader READER_TO_USE
|
|
363
345
|
```
|
|
364
346
|
|
|
365
347
|
Use `jukebox-admin` for admin commands:
|
|
@@ -375,15 +357,7 @@ uv run --extra api jukebox-admin api
|
|
|
375
357
|
uv run --extra ui jukebox-admin ui
|
|
376
358
|
```
|
|
377
359
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
```shell
|
|
381
|
-
uv run discstore settings show
|
|
382
|
-
uv run --extra api discstore api
|
|
383
|
-
uv run --extra ui discstore ui
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
Other commands are available:
|
|
360
|
+
### Development commands
|
|
387
361
|
|
|
388
362
|
| Command | Description |
|
|
389
363
|
| --- | --- |
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Jukebox \[gukebox\]
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[
|
|
4
|
+
[](https://pypi.python.org/pypi/gukebox)
|
|
5
|
+
[](https://pypi.python.org/pypi/gukebox)
|
|
5
6
|
[](https://pypi.python.org/pypi/gukebox)
|
|
6
7
|
[](https://github.com/gudsfile/jukebox/actions)
|
|
7
8
|
[](https://github.com/astral-sh/uv)
|
|
@@ -12,7 +13,7 @@
|
|
|
12
13
|
|
|
13
14
|
🚧 At the moment:
|
|
14
15
|
|
|
15
|
-
- NFC tags - CDs must be pre-populated in a JSON file (`
|
|
16
|
+
- NFC tags - CDs must be pre-populated in a JSON file (`jukebox-admin` included with `jukebox` may be of help to you)
|
|
16
17
|
- supports many music providers (Spotify, Apple Music, etc.), just add the URIs to the JSON file
|
|
17
18
|
- only works with Sonos speakers (there is a "dryrun" player for development), but code is designed to **add new ones**
|
|
18
19
|
- **as soon as** the NFC tag is removed, the music pauses, then resumes when the NFC tag is replaced
|
|
@@ -26,10 +27,9 @@
|
|
|
26
27
|
|
|
27
28
|
- [Install](#install)
|
|
28
29
|
- [First steps](#first-steps)
|
|
29
|
-
- [Discstore](#manage-the-library-with-the-discstore)
|
|
30
30
|
- [Usage](#usage)
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
- [Readers](#readers)
|
|
32
|
+
- [Players](#players)
|
|
33
33
|
- [The library file](#the-library-file)
|
|
34
34
|
- [Developer setup](#developer-setup)
|
|
35
35
|
|
|
@@ -103,18 +103,18 @@ uv sync
|
|
|
103
103
|
|
|
104
104
|
## First steps
|
|
105
105
|
|
|
106
|
-
Initialize the library file with `
|
|
106
|
+
Initialize the library file with `jukebox-admin` or manually create it at `~/.config/jukebox/library.json`.
|
|
107
107
|
|
|
108
|
-
### Manage the library with the
|
|
108
|
+
### Manage the library with the Admin CLI
|
|
109
109
|
|
|
110
110
|
To associate an URI with an NFC tag:
|
|
111
111
|
|
|
112
112
|
```shell
|
|
113
|
-
|
|
113
|
+
jukebox-admin library add tag_id --uri /path/to/media.mp3
|
|
114
114
|
```
|
|
115
115
|
or to pull the `tag_id` currently on the reader:
|
|
116
116
|
```shell
|
|
117
|
-
|
|
117
|
+
jukebox-admin library add --from-current --uri /path/to/media.mp3
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
Other commands are available, use `--help` to see them.
|
|
@@ -146,8 +146,6 @@ uv run --extra api jukebox-admin api
|
|
|
146
146
|
uv run --extra ui jukebox-admin ui
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
`discstore settings ...`, `discstore api`, and `discstore ui` remain available as compatibility commands, but `jukebox-admin` is the preferred CLI for admin flows.
|
|
150
|
-
|
|
151
149
|
### Manage the library manually
|
|
152
150
|
|
|
153
151
|
Complete your `~/.config/jukebox/library.json` file with each tag id and the expected media URI.
|
|
@@ -158,16 +156,18 @@ Take a look at `library.example.json` and the [The library file](#the-library-fi
|
|
|
158
156
|
Start the jukebox with the `jukebox` command (show help message with `--help`)
|
|
159
157
|
|
|
160
158
|
```shell
|
|
161
|
-
jukebox
|
|
159
|
+
jukebox --player PLAYER --reader READER
|
|
162
160
|
```
|
|
163
161
|
|
|
164
162
|
🎉 With choosing the `sonos` player and `pn532` reader, by approaching a NFC tag stored in the `library.json` file, you should hear the associated music begins.
|
|
165
163
|
|
|
166
|
-
Optional Parameters
|
|
164
|
+
**Optional Parameters**
|
|
167
165
|
|
|
168
166
|
| Parameter | Description |
|
|
169
167
|
| --- | --- |
|
|
170
168
|
| `--help` | Show help message. |
|
|
169
|
+
| `--player PLAYER` | Player to use (`sonos`, `dryrun`). |
|
|
170
|
+
| `--reader READER` | Reader to use (`pn532`, `dryrun`). |
|
|
171
171
|
| `--library` | Path to the library file, default: `~/.config/jukebox/library.json`. |
|
|
172
172
|
| `--pause-delay SECONDS` | Grace period before pausing when the NFC tag is removed. Fractional values such as `0.5` or `0.2` are supported, with a minimum of `0.2` seconds to avoid pausing on brief missed reads. Default: 0.25 seconds. |
|
|
173
173
|
| `--pause-duration SECONDS` | Maximum duration of a pause before resetting the queue. Default: 900 seconds (15 minutes). |
|
|
@@ -176,40 +176,29 @@ Optional Parameters
|
|
|
176
176
|
|
|
177
177
|
### Readers
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
- tag_id: the full identifier of the tag, in the format required by the system
|
|
184
|
-
- duration_seconds: a non-negative number of seconds used to simulate how long the tag remains in place. Fractional values are allowed.
|
|
185
|
-
Complete example: `your:tag:uid 2.5`
|
|
179
|
+
| Name | Description |
|
|
180
|
+
| --- | --- |
|
|
181
|
+
| Dry Run (`dryrun`) | Simulates NFC tag reading via stdin. Input format: `tag_id` or `tag_id duration_seconds`. |
|
|
182
|
+
| Pn532 NFC (`pn532`) | Reads physical NFC tags. Works with a **PN532** reader and **NTAG2xx** tags. Requires the `pn532` extra and SPI enabled on the Raspberry Pi. |
|
|
186
183
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
This project works with an NFC reader like the **PN532** and NFC tags like the **NTAG2xx**.
|
|
190
|
-
It is configured according to the [Waveshare PN532 wiki](https://www.waveshare.com/wiki/PN532_NFC_HAT).
|
|
191
|
-
Don't forget to enable the SPI interface using the command `sudo raspi-config`, then go to: `Interface Options > SPI > Enable > Yes`.
|
|
184
|
+
> [!NOTE]
|
|
185
|
+
> See [docs/readers.md](docs/readers.md) for full setup, hardware requirements, and settings reference.
|
|
192
186
|
|
|
193
187
|
### Players
|
|
194
188
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
Play music through a Sonos speaker.
|
|
200
|
-
Three ways to select the speaker (mutually exclusive):
|
|
189
|
+
| Name | Description |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| Dry Run (`dryrun`) | Displays the events that a real speaker would have performed (`playing …`, `pause`, etc.). |
|
|
192
|
+
| Sonos (`sonos`) | [](https://github.com/SoCo/SoCo) Plays music through a Sonos speaker. Select by IP (`--sonos-host`), by name (`--sonos-name`), or let it auto-discover. |
|
|
201
193
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
| By IP | `--sonos-host 192.168.0.x` | `JUKEBOX_SONOS_HOST` | Connect directly, no discovery |
|
|
205
|
-
| By name | `--sonos-name "Living Room"` | `JUKEBOX_SONOS_NAME` | Discover, then filter by name (case-sensitive) |
|
|
206
|
-
| Auto | *(omit both)* | *(omit both)* | Discover, pick the first speaker alphabetically |
|
|
194
|
+
> [!NOTE]
|
|
195
|
+
> See [docs/players.md](docs/players.md) for the full configuration reference.
|
|
207
196
|
|
|
208
197
|
## The library file
|
|
209
198
|
|
|
210
199
|
The `library.json` file is a JSON file that contains the artists, albums and tags.
|
|
211
200
|
It is used by the `jukebox` command to find the corresponding metadata for each tag.
|
|
212
|
-
And the `
|
|
201
|
+
And the `jukebox-admin library` command help you to managed this file with a CLI, an interactive CLI, an API or an UI (see `jukebox-admin --help`).
|
|
213
202
|
|
|
214
203
|
By default, this file should be placed at `~/.config/jukebox/library.json`. But you can use another path by creating a `JUKEBOX_LIBRARY_PATH` environment variable or with the `--library` argument.
|
|
215
204
|
|
|
@@ -283,9 +272,7 @@ uv sync
|
|
|
283
272
|
|
|
284
273
|
Add `--all-extras` to install dependencies for all extras (`api` and `ui`).
|
|
285
274
|
|
|
286
|
-
If needed,
|
|
287
|
-
If neither is set, the jukebox will auto-discover a speaker on the network.
|
|
288
|
-
To do this you can use a `.env` file and `uv run --env-file .env <command to run>`.
|
|
275
|
+
If needed, you can use a `.env` file and `uv run --env-file .env <command to run>`.
|
|
289
276
|
A `.env.example` file is available, you can copy it and modify it to use it.
|
|
290
277
|
|
|
291
278
|
Create a `library.json` file and complete it with the desired NFC tags and CDs.
|
|
@@ -296,12 +283,7 @@ Take a look at `library.example.json` and the [The library file](#the-library-fi
|
|
|
296
283
|
Start the jukebox with `uv` and use `--help` to show help message
|
|
297
284
|
|
|
298
285
|
```shell
|
|
299
|
-
uv run jukebox PLAYER_TO_USE READER_TO_USE
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
Start the discstore `uv` and use `--help` to show help message
|
|
303
|
-
```shell
|
|
304
|
-
uv run discstore --help
|
|
286
|
+
uv run jukebox --player PLAYER_TO_USE --reader READER_TO_USE
|
|
305
287
|
```
|
|
306
288
|
|
|
307
289
|
Use `jukebox-admin` for admin commands:
|
|
@@ -317,15 +299,7 @@ uv run --extra api jukebox-admin api
|
|
|
317
299
|
uv run --extra ui jukebox-admin ui
|
|
318
300
|
```
|
|
319
301
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
```shell
|
|
323
|
-
uv run discstore settings show
|
|
324
|
-
uv run --extra api discstore api
|
|
325
|
-
uv run --extra ui discstore ui
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
Other commands are available:
|
|
302
|
+
### Development commands
|
|
329
303
|
|
|
330
304
|
| Command | Description |
|
|
331
305
|
| --- | --- |
|
{gukebox-1.0.0.dev7 → gukebox-1.0.0.dev9}/discstore/adapters/inbound/api/current_tag_router.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Any, Optional
|
|
2
2
|
|
|
3
3
|
from fastapi import APIRouter, HTTPException, Response, status
|
|
4
|
+
from pydantic import ValidationError
|
|
4
5
|
|
|
5
6
|
from discstore.adapters.inbound.api.models import (
|
|
6
7
|
CurrentTagDiscOutput,
|
|
@@ -92,8 +93,8 @@ def build_current_tag_router(
|
|
|
92
93
|
|
|
93
94
|
try:
|
|
94
95
|
new_disc = Disc(**disc.model_dump())
|
|
95
|
-
add_disc.execute(current_tag_status.tag_id, new_disc)
|
|
96
|
-
return build_current_tag_disc_output(current_tag_status.tag_id,
|
|
96
|
+
created_disc = add_disc.execute(current_tag_status.tag_id, new_disc)
|
|
97
|
+
return build_current_tag_disc_output(current_tag_status.tag_id, created_disc)
|
|
97
98
|
except ValueError as value_err:
|
|
98
99
|
raise HTTPException(status_code=409, detail=str(value_err))
|
|
99
100
|
except Exception as err:
|
|
@@ -121,14 +122,16 @@ def build_current_tag_router(
|
|
|
121
122
|
try:
|
|
122
123
|
metadata = None
|
|
123
124
|
if disc_patch.metadata is not None:
|
|
124
|
-
metadata = DiscMetadata(**disc_patch.metadata.model_dump(exclude_unset=True
|
|
125
|
+
metadata = DiscMetadata(**disc_patch.metadata.model_dump(exclude_unset=True))
|
|
125
126
|
|
|
126
127
|
option = None
|
|
127
128
|
if disc_patch.option is not None:
|
|
128
|
-
option = DiscOption(**disc_patch.option.model_dump(exclude_unset=True
|
|
129
|
+
option = DiscOption(**disc_patch.option.model_dump(exclude_unset=True))
|
|
129
130
|
|
|
130
|
-
edit_disc.execute(current_tag_status.tag_id, disc_patch.uri, metadata, option)
|
|
131
|
-
return build_current_tag_disc_output(current_tag_status.tag_id,
|
|
131
|
+
updated_disc = edit_disc.execute(current_tag_status.tag_id, disc_patch.uri, metadata, option)
|
|
132
|
+
return build_current_tag_disc_output(current_tag_status.tag_id, updated_disc)
|
|
133
|
+
except ValidationError as err:
|
|
134
|
+
raise HTTPException(status_code=422, detail=err.errors())
|
|
132
135
|
except ValueError as value_err:
|
|
133
136
|
raise HTTPException(status_code=404, detail=str(value_err))
|
|
134
137
|
except Exception as err:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Dict
|
|
2
2
|
|
|
3
3
|
from fastapi import APIRouter, HTTPException, Response, status
|
|
4
|
+
from pydantic import ValidationError
|
|
4
5
|
|
|
5
6
|
from discstore.adapters.inbound.api.models import DiscInput, DiscOutput, DiscPatchInput
|
|
6
7
|
from discstore.domain.entities import Disc, DiscMetadata, DiscOption
|
|
@@ -48,13 +49,15 @@ def build_discs_router(
|
|
|
48
49
|
try:
|
|
49
50
|
metadata = None
|
|
50
51
|
if disc_patch.metadata is not None:
|
|
51
|
-
metadata = DiscMetadata(**disc_patch.metadata.model_dump(exclude_unset=True
|
|
52
|
+
metadata = DiscMetadata(**disc_patch.metadata.model_dump(exclude_unset=True))
|
|
52
53
|
|
|
53
54
|
option = None
|
|
54
55
|
if disc_patch.option is not None:
|
|
55
|
-
option = DiscOption(**disc_patch.option.model_dump(exclude_unset=True
|
|
56
|
+
option = DiscOption(**disc_patch.option.model_dump(exclude_unset=True))
|
|
56
57
|
|
|
57
58
|
return edit_disc.execute(tag_id, disc_patch.uri, metadata, option)
|
|
59
|
+
except ValidationError as err:
|
|
60
|
+
raise HTTPException(status_code=422, detail=err.errors())
|
|
58
61
|
except ValueError as value_err:
|
|
59
62
|
raise HTTPException(status_code=404, detail=str(value_err))
|
|
60
63
|
except Exception as err:
|
|
@@ -23,7 +23,7 @@ except ModuleNotFoundError as e:
|
|
|
23
23
|
if e.name != "fastapi":
|
|
24
24
|
raise
|
|
25
25
|
raise ModuleNotFoundError(
|
|
26
|
-
optional_extra_dependency_message("The `api_controller` module", "api", "
|
|
26
|
+
optional_extra_dependency_message("The `api_controller` module", "api", "jukebox-admin api")
|
|
27
27
|
) from e
|
|
28
28
|
from discstore.domain.use_cases.add_disc import AddDisc
|
|
29
29
|
from discstore.domain.use_cases.edit_disc import EditDisc
|
|
@@ -108,8 +108,8 @@ class APIController:
|
|
|
108
108
|
self.settings_service = settings_service
|
|
109
109
|
self.sonos_service = sonos_service
|
|
110
110
|
self.app = FastAPI(
|
|
111
|
-
title="
|
|
112
|
-
description="API for managing Jukebox disc library",
|
|
111
|
+
title="Jukebox Admin API",
|
|
112
|
+
description="API for managing Jukebox disc library and settings",
|
|
113
113
|
docs_url="/docs",
|
|
114
114
|
redoc_url="/redoc",
|
|
115
115
|
)
|