diff --git a/lib/services/audio_service.dart b/lib/services/audio_service.dart index 8be63c5..3d21d1b 100644 --- a/lib/services/audio_service.dart +++ b/lib/services/audio_service.dart @@ -62,14 +62,17 @@ class AudioService extends ChangeNotifier { bool _listening = false; final List _subscriptions = []; + // ---- 播放生命周期标志 ---- bool _handlingCompletion = false; - void Function(Song)? _onSongChanged; - + bool _isChangingTrack = false; + bool _hasStartedCurrentPlayback = false; bool _isUserSeeking = false; // ⭐ 播放代数:每次切歌递增,用于校验异步任务是否过期 int _playbackGeneration = 0; + void Function(Song)? _onSongChanged; + // ---- Repository ---- final SongDatabase _db = SongDatabase(); final PlaylistRepository _playlistRepo = PlaylistRepository(); @@ -123,7 +126,7 @@ class AudioService extends ChangeNotifier { } // ---- 设置播放队列 ---- - void setQueue(List queue, {int startIndex = 0}) { + Future setQueue(List queue, {int startIndex = 0}) async { if (queue.isEmpty) { _clearQueue(); return; @@ -132,15 +135,20 @@ class AudioService extends ChangeNotifier { _queue = List.from(queue); _currentIndex = startIndex.clamp(0, _queue.length - 1); - _shuffledIndices = List.generate(_queue.length, (i) => i); - _shuffledIndices.shuffle(); - _shuffledIndex = _shuffledIndices.indexOf(_currentIndex); - if (_shuffledIndex == -1) { - _shuffledIndex = 0; - _currentIndex = _shuffledIndices[0]; + if (_playMode == PlayMode.shuffle) { + _shuffledIndices = List.generate(_queue.length, (i) => i); + _shuffledIndices.shuffle(); + _shuffledIndex = _shuffledIndices.indexOf(_currentIndex); + if (_shuffledIndex == -1) { + _shuffledIndex = 0; + _currentIndex = _shuffledIndices[0]; + } + } else { + _shuffledIndices = List.generate(_queue.length, (i) => i); + _shuffledIndex = _currentIndex; } - _playCurrent(); + await _playCurrent(); } void _clearQueue() { @@ -156,9 +164,9 @@ class AudioService extends ChangeNotifier { Future playSong(Song song) async { _currentPlaylistId = null; if (_queue.isEmpty || _queue[_currentIndex].id != song.id) { - setQueue([song], startIndex: 0); + await setQueue([song], startIndex: 0); } else { - _playCurrent(); + await _playCurrent(); } } @@ -198,7 +206,7 @@ class AudioService extends ChangeNotifier { } if (songs.isNotEmpty) { - setQueue(songs, startIndex: startIndex); + await setQueue(songs, startIndex: startIndex); } } @@ -212,35 +220,81 @@ class AudioService extends ChangeNotifier { } // ════════════════════════════════════════════════════════════ - // ⭐ 播放核心(修复:自动添加认证头) + // ⭐ 播放核心(完整生命周期控制) // ════════════════════════════════════════════════════════════ - void _playCurrent() { + Future _playCurrent() async { + if (_isChangingTrack) { + debugPrint('⚠️ [AudioService] track switch already running'); + return; + } + if (_currentIndex < 0 || _currentIndex >= _queue.length) { stopPlay(); return; } - _playbackGeneration++; - final generation = _playbackGeneration; + // ⭐ 进入切歌状态 + _isChangingTrack = true; + _handlingCompletion = false; + _hasStartedCurrentPlayback = false; - final song = _queue[_currentIndex]; - _currentSong = song; + try { + _playbackGeneration++; + final generation = _playbackGeneration; - _startListening(); - notifyListeners(); + final song = _queue[_currentIndex]; + _currentSong = song; - if (song.url == null || song.url!.isEmpty) { + _startListening(); + notifyListeners(); + + if (song.url == null || song.url!.isEmpty) { + return; + } + + // ⭐ 关键:等待播放器加载完成 + await _playWithHeaders(song.url!); + + // ⭐ 等待播放真正开始 + await _waitForPlaybackStarted(); + + _syncPlayerStateDelayed(); + + // ⭐ 只有真正开始播放后才推送通知 + if (_hasStartedCurrentPlayback) { + _onSongChanged?.call(song); + _loadMetadataForCurrentSong(generation); + } + } finally { + _isChangingTrack = false; + } + } + + /// ⭐ 等待播放器真正开始播放 + Future _waitForPlaybackStarted() async { + final player = PlaybackService().player; + + // 如果已经在播放,直接标记 + if (player.state.playing) { + _hasStartedCurrentPlayback = true; + debugPrint('🎵 [AudioService] playback already started'); return; } - // ⭐ 关键修复:播放时自动添加认证头 - _playWithHeaders(song.url!); - - _syncPlayerStateDelayed(); - - _onSongChanged?.call(song); - _loadMetadataForCurrentSong(generation); + // 等待 playing 变为 true,超时 3 秒 + try { + await player.stream.playing + .where((playing) => playing == true) + .first + .timeout(const Duration(seconds: 3)); + _hasStartedCurrentPlayback = true; + debugPrint('🎵 [AudioService] playback started'); + } catch (e) { + debugPrint('⚠️ [AudioService] wait for playback timeout: $e'); + // 超时也标记为已开始,避免永久阻塞 + _hasStartedCurrentPlayback = true; + } } /// ⭐ 播放带认证头的 URL @@ -374,13 +428,13 @@ class AudioService extends ChangeNotifier { if (currentIndex >= 0 && currentIndex < _queue.length) { stopPlay(); - _playCurrent(); + await _playCurrent(); debugPrint('🔄 [AudioService] song reloaded'); } } // ---- 下一首 ---- - void next() { + Future next() async { if (_queue.isEmpty) return; if (_playMode == PlayMode.shuffle) { @@ -388,17 +442,17 @@ class AudioService extends ChangeNotifier { final nextIdx = (_shuffledIndex + 1) % _shuffledIndices.length; _shuffledIndex = nextIdx; _currentIndex = _shuffledIndices[nextIdx]; - _playCurrent(); + await _playCurrent(); return; } final nextIdx = (_currentIndex + 1) % _queue.length; _currentIndex = nextIdx; - _playCurrent(); + await _playCurrent(); } // ---- 上一首 ---- - void previous() { + Future previous() async { if (_queue.isEmpty) return; if (_playMode == PlayMode.shuffle) { @@ -410,7 +464,7 @@ class AudioService extends ChangeNotifier { _shuffledIndex = prevIdx; } _currentIndex = _shuffledIndices[_shuffledIndex]; - _playCurrent(); + await _playCurrent(); return; } @@ -420,7 +474,7 @@ class AudioService extends ChangeNotifier { } else { _currentIndex = prevIdx; } - _playCurrent(); + await _playCurrent(); } // ---- 播放/暂停 ---- @@ -494,6 +548,7 @@ class AudioService extends ChangeNotifier { }); } + // ---- 监听 media_kit 状态 ---- void _startListening() { if (_listening) return; _listening = true; @@ -534,8 +589,47 @@ class AudioService extends ChangeNotifier { }), ); + // ⭐ 完整生命周期校验的 completed 监听 _subscriptions.add( player.stream.completed.listen((_) { + // 第一层:切歌期间屏蔽 + if (_isChangingTrack) { + debugPrint('⚠️ [AudioService] completed ignored during track switch'); + return; + } + + // 第二层:从未真正开始播放,忽略 + if (!_hasStartedCurrentPlayback) { + debugPrint( + '⚠️ [AudioService] completed ignored: playback never started'); + return; + } + + // 第三层:防重入 + if (_handlingCompletion) { + debugPrint('⚠️ [service] completed ignored: already handling'); + return; + } + + // 第四层:额外校验 - 当前歌曲必须有有效的 duration + final pos = player.state.position; + final dur = player.state.duration; + if (dur.inMilliseconds <= 0) { + debugPrint( + '⚠️ [AudioService] completed ignored: invalid duration $dur'); + return; + } + + // 第五层:位置必须接近歌曲末尾(允许 500ms 误差) + if (pos.inMilliseconds < dur.inMilliseconds - 500) { + debugPrint( + '⚠️ [AudioService] completed ignored: position=$pos, duration=$dur, not at end'); + return; + } + + debugPrint( + '🎵 [AudioService] valid completed: index=$_currentIndex, pos=$pos, dur=$dur'); + _handlingCompletion = true; _onPlaybackCompleted(); }), ); @@ -549,14 +643,23 @@ class AudioService extends ChangeNotifier { _subscriptions.clear(); } + // ---- 播放完成处理 ---- void _onPlaybackCompleted() { - if (_handlingCompletion) { - debugPrint('⚠️ [service] completed ignored: already handling'); + // 队列状态检查 + if (_queue.isEmpty || _currentIndex < 0 || _currentIndex >= _queue.length) { + debugPrint('⚠️ [service] completed ignored: invalid queue'); + _handlingCompletion = false; return; } - _handlingCompletion = true; + + // 如果是最后一首且不是单曲循环模式,不触发 next + if (_currentIndex + 1 >= _queue.length && _playMode != PlayMode.repeatOne) { + debugPrint('⚠️ [service] completed ignored: last song in queue'); + _handlingCompletion = false; + return; + } + try { - if (_queue.isEmpty) return; if (_playMode == PlayMode.repeatOne) { _playCurrent(); return;