major bug fixes
This commit is contained in:
parent
9728906556
commit
d6144c4483
39 changed files with 483 additions and 53 deletions
|
|
@ -595,6 +595,33 @@ final activePlaybackProvider = Provider<PlaybackState>((ref) {
|
|||
return remote ?? ref.watch(playbackProvider);
|
||||
});
|
||||
|
||||
/// Keeps the theme accent in sync while acting as a remote (bug #6).
|
||||
///
|
||||
/// On the controlling device the local playback engine never loads the track,
|
||||
/// so its `onArt` extraction (see [playbackControllerProvider]) never fires and
|
||||
/// the accent would stay at the default. Here we re-extract the accent from the
|
||||
/// mirrored remote track's cover art — the controlling device is connected to
|
||||
/// the same server, so it can fetch the art itself. Selecting on `coverArt`
|
||||
/// (a value-equal String) means this only fires on an actual art change, not on
|
||||
/// the ~1/s snapshot churn. Watched in `main.dart` so it stays alive.
|
||||
final remoteAccentSyncProvider = Provider<void>((ref) {
|
||||
ref.listen<String?>(
|
||||
remoteControlProvider.select(
|
||||
(s) => s.isAttached ? s.remoteState?.current?.coverArt : null,
|
||||
),
|
||||
(prev, coverArt) async {
|
||||
if (ref.read(settingsProvider).useStaticAccent) return;
|
||||
if (coverArt == null) return;
|
||||
final client = ref.read(subsonicClientProvider);
|
||||
if (client == null) return;
|
||||
final uri = client.coverArtUri(coverArt, size: 512);
|
||||
final color = await extractAccent(NetworkImage(uri.toString()));
|
||||
if (color != null) ref.read(accentProvider.notifier).set(color);
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
});
|
||||
|
||||
/// The command sink the UI should drive — a proxy that serializes commands over
|
||||
/// the LAN to the attached device when acting as a remote, else the local
|
||||
/// [PlaybackController]. Widgets read this instead of `playbackProvider.notifier`.
|
||||
|
|
|
|||
|
|
@ -209,10 +209,12 @@ class RemoteControlController extends StateNotifier<RemoteControlState> {
|
|||
|
||||
_statusSub = session.status.listen((st) {
|
||||
_update((s) => s.copyWith(status: st));
|
||||
// Any terminal status drops us back to local playback.
|
||||
if (st == RemoteConnStatus.disconnected ||
|
||||
st == RemoteConnStatus.error ||
|
||||
st == RemoteConnStatus.denied) {
|
||||
// Only a terminal status drops us back to local playback. Transient
|
||||
// drops (phone locked, Wi-Fi blip) are healed inside the session, which
|
||||
// retries and re-emits `connecting` — we stay attached (bug-fixes #5).
|
||||
// `denied` is a hard rejection; `error` here only means a device that
|
||||
// never answered the initial dial.
|
||||
if (st == RemoteConnStatus.denied || st == RemoteConnStatus.error) {
|
||||
// Defer so we're not tearing the session down inside its own callback.
|
||||
scheduleMicrotask(detach);
|
||||
}
|
||||
|
|
@ -225,6 +227,13 @@ class RemoteControlController extends StateNotifier<RemoteControlState> {
|
|||
await session.connect();
|
||||
}
|
||||
|
||||
/// Re-establish a dropped remote session immediately when the app returns to
|
||||
/// the foreground. A controller is suspended while the phone is locked, so
|
||||
/// its socket to the host is usually dead on resume; without this nudge the
|
||||
/// user would wait out the reconnect backoff before control is restored
|
||||
/// (bug-fixes #5). No-op when playing locally.
|
||||
void onResume() => _session?.reconnectNow();
|
||||
|
||||
Future<void> detach() async {
|
||||
await _snapSub?.cancel();
|
||||
_snapSub = null;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue