network addition
This commit is contained in:
parent
3bd713d667
commit
2099d3d64d
27 changed files with 2237 additions and 40 deletions
|
|
@ -6,7 +6,7 @@ These are ranked in order of importance and should be tackled one element at a t
|
|||
|
||||
- [x] 1. Drag n Drop Queue Reorder
|
||||
- [x] 2. Filtering and Sorting Options
|
||||
- [ ] 3. Network Connectivity
|
||||
- [x] 3. Network Connectivity
|
||||
- [x] 4. Simoultaneous Download Setting
|
||||
- [x] 5. Playlist Sharing
|
||||
- [ ] 6. In-App Purchase
|
||||
|
|
@ -65,5 +65,33 @@ These are ranked in order of importance and should be tackled one element at a t
|
|||
### 6. In-App "Buy the Dev a Coffee" ($5) — Medium, client + store config
|
||||
- **Resolved:** route through IAP (review-safe). Flutter `in_app_purchase`, modeled as a **consumable** $5 product configured in App Store Connect + Google Play Console. Apple requires digital tips to go through IAP (no external payment link on iOS). Optional button in `lib/screens/settings_screen.dart`.
|
||||
|
||||
### 3. Network Connectivity / Cross-Device Control — Deferred deep-dive
|
||||
Biggest lift. Client-only + Subsonic offers no shared-state or device-to-device channel, and the app has zero realtime code. Candidate directions to evaluate in a dedicated session: (a) Subsonic **jukebox mode** (server-side playback control, not another client — limited), (b) **LAN mDNS discovery + direct socket** (no server, same-Wi-Fi only), (c) relay service (would break client-only). No work until scoped separately.
|
||||
### 3. Network Connectivity / Cross-Device Control — Large, LAN-only
|
||||
|
||||
**Scope (locked):** LAN-only · remote-control topology (one device plays audio, others act as remotes) · full remote (transport + live queue mirror + browse/enqueue/reorder). Both devices must be signed into the **same Subsonic server** — that constraint is what makes full remote cheap: the remote sends a track by `id` / `Song` JSON and the *host* resolves its own stream URI locally via the existing `streamUriFor`. No server changes; discovery + transport are pure LAN.
|
||||
|
||||
**Why not cross-network:** Subsonic/Navidrome exposes no realtime channel, pub/sub, or shared scratch space, so two instances can't coordinate over the internet without a relay (breaks client-only) or a hacky high-latency polling abuse of playlists. LAN-only is the robust client-only answer.
|
||||
|
||||
**Architecture**
|
||||
- **Transport:** host runs an in-process WebSocket server (`dart:io` `HttpServer` + `WebSocketTransformer.upgrade` — no new dep); remote is a `WebSocket.connect` client.
|
||||
- **Discovery:** mDNS/Bonjour via a new dep — `nsd` (register + browse; `bonsoir` is the alt). Service type `_timbre._tcp`; TXT record carries `serverKey`, protocol version, device name.
|
||||
- **Auth (no manual pairing):** both devices already know the server password → derive an HMAC key from it; host sends a nonce, remote returns `HMAC-SHA256(nonce)`. Proves same-user without a PIN (`crypto` already a dep). Control gated to matching `serverKey` only.
|
||||
- **Playback facade (load-bearing refactor):** `activePlaybackProvider` (state the UI renders) + `playbackCommandsProvider` (a `PlaybackCommands` interface the UI drives). `PlaybackController` (unchanged local engine) and a new `RemotePlaybackProxy` both satisfy the seam. Local mode delegates to the controller; remote mode renders the last WS snapshot and turns commands into outgoing messages (optimistic, reconciled on next snapshot).
|
||||
- **New modules:** `lib/remote/messages.dart` (DTOs + codec + auth), `host_server.dart`, `discovery.dart`, `remote_session.dart`, `remote_playback.dart`, `lib/state/remote_providers.dart`, plus a "Devices" picker sheet + a "Playing on <device>" status chip.
|
||||
|
||||
**Build order**
|
||||
1. **Protocol + codec + auth** — pure Dart, fully unit-testable (`Command` / `StateSnapshot` / `Hello` DTOs, versioning, HMAC challenge-response).
|
||||
2. **Facade indirection** — add `activePlaybackProvider` + `playbackCommandsProvider`, migrate the ~11 UI files off `playbackProvider`. Ships as a no-op local-only refactor; de-risks everything downstream. **← land first.**
|
||||
3. **Host server** — WS server broadcasts snapshots on `PlaybackController` change (reuse the throttle pattern), applies inbound commands.
|
||||
4. **Discovery** — advertise when playable / browse for peers; platform config.
|
||||
5. **Remote session + routing** — connect, mirror state, send commands, position interpolation, disconnect → fall back to local.
|
||||
6. **UI** — device sheet, status chip, route browse/enqueue actions to the host.
|
||||
7. **Auth + polish + tests** — loopback (host + client in one process) integration test.
|
||||
|
||||
**Risks / gotchas**
|
||||
- **iOS 14+:** `NSLocalNetworkUsageDescription` + `NSBonjourServices=_timbre._tcp` in Info.plist or discovery silently fails. Android NSD needs a multicast lock (nsd handles).
|
||||
- **iOS backgrounding:** host stays alive while *playing* (audio background mode via `just_audio_background`); a paused+backgrounded host may suspend its socket — remote reconnects on resume.
|
||||
- **Position sync:** interpolate on the remote between periodic snapshots; reconcile on seek.
|
||||
- **Offline host:** host resolves its own URIs; a track it can't stream reuses the existing `PlaybackError.offline` path.
|
||||
- Multi-remote works free: host broadcasts to all clients.
|
||||
|
||||
**Estimate:** ~2–3× any other item on this list; step 2 (facade) is the reversible foundation to land first.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue