From 7b7efc126d70747468287b29651e5a01d8c52c4c Mon Sep 17 00:00:00 2001 From: Nash Aschenbath Date: Fri, 13 Nov 2026 10:43:12 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E5=81=9C=E6=AD=A2=E5=90=8E=E7=9A=84=E7=BB=AD=E6=92=AD=E5=92=8C?= =?UTF-8?q?=E6=97=A7=E5=AE=9A=E6=97=B6=E5=8A=A8=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/services/player_service.dart | 34 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/lib/services/player_service.dart b/lib/services/player_service.dart index 248ea81..3e6eeee 100644 --- a/lib/services/player_service.dart +++ b/lib/services/player_service.dart @@ -132,6 +132,9 @@ class PlayerService { late final StreamSubscription _driverEvents; late final StreamSubscription _downloadEvents; Timer? _timer; + int _sleepRevision = 0; + // A queued expiry blocks advancement, but cancellation can still revoke it. + int? _pendingSleepRevision; AudioProxy? _proxy; Future? _proxyOpening; PlaybackSource? _source; @@ -173,6 +176,7 @@ class PlayerService { int _playbackRecoveryRevision = 0; Stream get changes => _changes.stream; + bool get _sleepStopping => _sleepStopped || _pendingSleepRevision != null; List get queue => List.unmodifiable(_queue); Track? get current => cursor >= 0 && cursor < _queue.length ? _queue[cursor] : null; @@ -1348,7 +1352,7 @@ class PlayerService { final source = _source; if (source == null || _atEnd || - _sleepStopped || + _sleepStopping || !snapshot.playing || snapshot.buffering || snapshot.duration <= 0 || @@ -1369,7 +1373,7 @@ class PlayerService { bool active() => !_closed && !_atEnd && - !_sleepStopped && + !_sleepStopping && playing && request == _request && epoch == _epoch && @@ -1492,10 +1496,13 @@ class PlayerService { // admission now, so it cannot start audio before the queued sleep action. final expired = policy.pollSleep(); if (expired != null) { + final revision = _sleepRevision; _newRequest(); - _sleepStopped = true; + _pendingSleepRevision = revision; await _commands.synchronized(() async { - if (!_closed) await _sleep(expired, invalidateRequest: false); + if (_closed || revision != _sleepRevision) return; + _pendingSleepRevision = null; + await _sleep(expired, invalidateRequest: false); }); return; } @@ -1531,7 +1538,7 @@ class PlayerService { return; } await _pause(); - if (_manualPaused || intent != _advanceIntent) return; + if (_manualPaused || _sleepStopping || intent != _advanceIntent) return; if (effectiveSettings.loopMode == 'single') { await _openCurrent(_newRequest(), resumePosition: 0); return; @@ -1557,7 +1564,7 @@ class PlayerService { _closed || !_atEnd || _manualPaused || - _sleepStopped || + _sleepStopping || epoch != _epoch || request != _request || intent != _advanceIntent || @@ -1745,7 +1752,7 @@ class PlayerService { _endAdvanceIntent == _advanceIntent && current?.id == last.id && cursor + 1 < _queue.length && - !_sleepStopped && + !_sleepStopping && !_manualPaused && effectiveSettings.autoNext && effectiveSettings.loopMode != 'single') { @@ -1766,7 +1773,7 @@ class PlayerService { if (speculative && (!_atEnd || _manualPaused || - _sleepStopped || + _sleepStopping || endEpoch != _epoch || advanceIntent != _advanceIntent)) rethrow; @@ -1799,11 +1806,15 @@ class PlayerService { } void setSleep(SleepRequest request) { + ++_sleepRevision; + _pendingSleepRevision = null; policy.armSleep(request); _notify(); } void cancelSleep() { + ++_sleepRevision; + _pendingSleepRevision = null; policy.cancelSleep(); _notify(); } @@ -1812,6 +1823,7 @@ class PlayerService { SleepAction action, { bool invalidateRequest = true, }) async { + final revision = _sleepRevision; if (invalidateRequest) _newRequest(); _sleepStopped = true; try { @@ -1823,13 +1835,17 @@ class PlayerService { rethrow; } } + // Saving the stopped state can outlive a cancelled or replacement timer. + if (revision != _sleepRevision) return; status = '定时已结束'; _notify(); if (action == SleepAction.exit) { // Called outside the command lock: normal application close quiesces us. final exit = exitApplication; if (exit == null) throw StateError('未配置应用退出处理'); - scheduleMicrotask(() => _background(exit)); + scheduleMicrotask(() { + if (revision == _sleepRevision) _background(exit); + }); } }