mobile-music/updates-features.md
2026-08-04 16:39:18 -04:00

97 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Updates and Features
These are ranked in order of importance and should be tackled one element at a time before moving onto the next one. This stands as the top level orchestration for improving the application.
### Completion Checklist
- [x] 1. Drag n Drop Queue Reorder
- [x] 2. Filtering and Sorting Options
- [x] 3. Network Connectivity
- [x] 4. Simoultaneous Download Setting
- [x] 5. Playlist Sharing
- [ ] 6. In-App Purchase
### Details
1. Drag n Drop Queue Reorder
Currently the queue only allows the user to add utilizing the dedicated buttons throughout the app or remove elements using the 'X' option in the queue itself. Once an item has been added to the queue there is no method of moving it to another point. For example, if I decided I wwanted to shift the next song dowwn a handful of places in the queue it is currently not possible. We should add a drag and drop functionality to reorder elements in the queue.
2. Filtering and Sorting Options
Within the Albums and Tracks section of the app library there should be more options for sorting and filtering. I would like the ability to filter based on genre, release year, and when it wwas added to the server. That wwway it can be easier to find what I am looking for without have to directly search for anything. All of those elements should be available in the metadata.
3. Network Connectivity
This is probably the largest task of this group. I wwwant there to be connectivity through instances of the application. For example I have the app on my ipad as ell as my phone nd I would like the ability to control the device that is currently playing with the other. Basically a shared state in the app that allows for wireless control. This is a common feature of all music apps and should be included in ours as well.
4. Simoultaneous Download Setting
Currently the app will only dowwnload 3 songs concurrently. There should be an option in settings to specify the number of concurrent tracks that can be dowwnloaded at a time to help speed up the process. In conjunction with this there should be an option on tracks to download all tracks.
5. Playlist Sharing
I have multiple users on my server that I have active in the application and I want to be able to share playlists amongst us all. Given the fact we are sharing the same server this should be possible. This wwwill require thinking to figure out the guardrails and application of sharing but I wwant this feature.
6. In-App Purchase
I want a "Buy the dev a coffee" type payment availbale in the app. Android this is not too much of a concern for review etc but for Apple IOS review it wmight be more of a hassle. Basically I want an optional button/toggle in settings that would let a user pay 5$ for the app voluntarily to help with its development.
---
## Execution Plan
**Stack:** Flutter/Dart · Riverpod · dio · just_audio · Subsonic API (Navidrome). **Hard constraint: client-only** — no server changes; every feature must ride existing Subsonic endpoints or live entirely in-app.
**Build order:** `1 → 4 → 2 → 5 → 6 → (3 as its own project)`. (Reordered from #4 before #2 — #4 is the smallest win and warms up the settings plumbing #2 reuses.)
### 1. Drag-n-Drop Queue Reorder — Small, pure client
- Add `reorderQueue(oldIndex, newIndex)` to `PlaybackController` (`lib/playback/playback_engine.dart`): move item in `queue`, adjust `currentIndex` so the now-playing track keeps playing, mirror the move onto just_audio's `ConcatenatingAudioSource` (reuse the existing `_applyOrder` path).
- Swap queue UI to `ReorderableListView`.
- **Resolved semantics:** reorder mutates the currently-active order. Reorder-then-shuffle → shuffle re-randomizes upcoming tail as normal. Shuffle-then-reorder → the manual move sticks, rest stays shuffled. (Open edge case: whether a reorder made while shuffled should also rewrite `_unshuffledOrder` so it survives un-shuffle — deferred.)
### 4. Simultaneous Download Setting + Download-All — Small, pure client
- Replace `static const int _maxConcurrent = 3` (`lib/downloads/download_manager.dart:141`) with a settings-backed value; ensure the pump loop re-reads it so changes apply live. Sane cap ~1–10.
- Add stepper setting in `lib/screens/settings_screen.dart` + `lib/settings/settings_store.dart`.
- Add "Download all" bulk-enqueue actions on album / playlist / tracks contexts.
### 2. Filtering & Sorting (genre / year / date-added) — Medium, client-only feasible
- **Prereq:** parse `created` (date-added) into Album/Song models in `lib/subsonic/models.dart` — not present today, required for "when added."
- **Albums:** extend `getAlbumList2` (`lib/subsonic/subsonic_client.dart`) to pass `genre`, `fromYear`/`toYear`; use `type=newest` for date-added; add `getGenres` for the genre picker.
- **Tracks:** sort/filter client-side over cached `lib/library/library_index.dart` (currently alphabetical-only).
- Add filter/sort control bar to `lib/screens/browser_screen.dart` panels; persist last choice in `settings_store.dart`.
- **Risks:** genre is free-text (casing / multi-genre inconsistency); server-side album lists paginate at 500.
### 5. Playlist Sharing (same-server users) — Medium, client-only feasible
- `Playlist` model already exposes `owner` + `public` (`lib/subsonic/models.dart:253-263`).
- Add `updatePlaylist(public: true)` support to client; add a "Share / make public" toggle in playlist UI. Surface a "Shared" section for playlists where `owner != currentUser`.
- **Resolved:** public (server-wide) sharing is acceptable. A non-owner **saves a copy** — clone the public playlist into their own via `createPlaylist` + `addTracksToPlaylist` (avoids edit-permission issues; owner-only edit/delete on originals).
- **Risks:** server-dependent — confirm Navidrome honors `public`. No targeted per-user sharing possible client-only (all-or-public only).
### 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 — 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.