From dea131778c7e2fe405da0f5bacb9a298566ef2bc Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Sun, 18 Oct 2026 00:46:53 +0700 Subject: [PATCH] refactor(playback): share lost route pause transitions --- lib/services/music_playback_deck.dart | 18 +++++++++--------- test/music_player_automix_test.dart | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lib/services/music_playback_deck.dart b/lib/services/music_playback_deck.dart index 97a4cdbd..f6a4581a 100644 --- a/lib/services/music_playback_deck.dart +++ b/lib/services/music_playback_deck.dart @@ -334,6 +334,12 @@ class MusicPlaybackDeck { if (!_disposed) _states.add(value); } + void _pauseLostRoute(UsbAudioStatus status) { + _routeLost = true; + _setState(PlayerState.paused); + usbAudioStatus.value = status; + } + Future resume() async { if (!_direct) return _ordinary.resume(); if (_volumeBlocked) { @@ -351,23 +357,17 @@ class MusicPlaybackDeck { } on PlatformException { if (token != _token || !_direct || _disposed) return; if (_preparedStatus.driver == 'usb_direct') { - _routeLost = true; - _setState(PlayerState.paused); - usbAudioStatus.value = const UsbAudioStatus(reason: 'route_changed'); + _pauseLostRoute(const UsbAudioStatus(reason: 'route_changed')); throw UsbOutputUnavailable( 'Direct USB output changed; reconnect before playing', ); } if (_nativeStarted) { - _routeLost = true; - _setState(PlayerState.paused); - usbAudioStatus.value = const UsbAudioStatus(reason: 'route_changed'); + _pauseLostRoute(const UsbAudioStatus(reason: 'route_changed')); rethrow; } if (_preparedStatus.transport != 'pcm') { - _routeLost = true; - _setState(PlayerState.paused); - usbAudioStatus.value = const UsbAudioStatus(reason: 'dsd_unsupported'); + _pauseLostRoute(const UsbAudioStatus(reason: 'dsd_unsupported')); throw UsbDsdUnavailable(); } final source = _source; diff --git a/test/music_player_automix_test.dart b/test/music_player_automix_test.dart index 60ec4abf..8418d3bd 100644 --- a/test/music_player_automix_test.dart +++ b/test/music_player_automix_test.dart @@ -636,6 +636,23 @@ void main() { expect(deck.needsSourceReload, isTrue); }); + test('failed first DoP start pauses without normal playback', () async { + transport = 'dop'; + failResume = true; + await deck.setSource( + DeviceFileSource('/one.dsf'), + preferBitPerfect: true, + allowDop: true, + ); + await expectLater(deck.resume(), throwsA(isA())); + expect(deck.state, PlayerState.paused); + expect(deck.needsSourceReload, isTrue); + expect(usbAudioStatus.value.reason, 'dsd_unsupported'); + expect(native.sources['usb-test'], isNull); + expect(native.playing, isEmpty); + expect(usbCalls, ['prepare', 'resume']); + }); + test('DSD without a DAC never loads a normal audio source', () async { fallbackReason = 'no_usb'; await expectLater( @@ -743,6 +760,8 @@ void main() { await expectLater(deck.resume(), throwsA(isA())); expect(native.sources['usb-test'], isNull); expect(native.playing, isEmpty); + expect(deck.state, PlayerState.paused); + expect(deck.needsSourceReload, isTrue); expect(usbAudioStatus.value.reason, 'route_changed'); }, ); @@ -755,14 +774,21 @@ void main() { preferBitPerfect: true, ); await deck.resume(); + var statusChanges = 0; + void onStatusChanged() => statusChanges++; + usbAudioStatus.addListener(onStatusChanged); + addTearDown(() => usbAudioStatus.removeListener(onStatusChanged)); await event('paused'); await _until(() => deck.state == PlayerState.paused); expect(native.playing, isEmpty); expect(usbAudioStatus.value.reason, 'route_changed'); + expect(statusChanges, 1); position = 19000; expect(deck.needsSourceReload, isTrue); await expectLater(deck.resume(), throwsA(isA())); expect(native.playing, isEmpty); + expect(deck.state, PlayerState.paused); + expect(statusChanges, 1); expect(await deck.getCurrentPosition(), const Duration(seconds: 19)); }, );