顺序播放跳曲与首次启动跳曲问题已部分修复,播放逻辑正确,metadata部分仍需修复
This commit is contained in:
+125
-22
@@ -62,14 +62,17 @@ class AudioService extends ChangeNotifier {
|
||||
bool _listening = false;
|
||||
final List<StreamSubscription> _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<Song> queue, {int startIndex = 0}) {
|
||||
Future<void> setQueue(List<Song> queue, {int startIndex = 0}) async {
|
||||
if (queue.isEmpty) {
|
||||
_clearQueue();
|
||||
return;
|
||||
@@ -132,6 +135,7 @@ class AudioService extends ChangeNotifier {
|
||||
_queue = List.from(queue);
|
||||
_currentIndex = startIndex.clamp(0, _queue.length - 1);
|
||||
|
||||
if (_playMode == PlayMode.shuffle) {
|
||||
_shuffledIndices = List.generate(_queue.length, (i) => i);
|
||||
_shuffledIndices.shuffle();
|
||||
_shuffledIndex = _shuffledIndices.indexOf(_currentIndex);
|
||||
@@ -139,8 +143,12 @@ class AudioService extends ChangeNotifier {
|
||||
_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<void> 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,15 +220,26 @@ class AudioService extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// ⭐ 播放核心(修复:自动添加认证头)
|
||||
// ⭐ 播放核心(完整生命周期控制)
|
||||
// ════════════════════════════════════════════════════════════
|
||||
|
||||
void _playCurrent() {
|
||||
Future<void> _playCurrent() async {
|
||||
if (_isChangingTrack) {
|
||||
debugPrint('⚠️ [AudioService] track switch already running');
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentIndex < 0 || _currentIndex >= _queue.length) {
|
||||
stopPlay();
|
||||
return;
|
||||
}
|
||||
|
||||
// ⭐ 进入切歌状态
|
||||
_isChangingTrack = true;
|
||||
_handlingCompletion = false;
|
||||
_hasStartedCurrentPlayback = false;
|
||||
|
||||
try {
|
||||
_playbackGeneration++;
|
||||
final generation = _playbackGeneration;
|
||||
|
||||
@@ -234,14 +253,49 @@ class AudioService extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
// ⭐ 关键修复:播放时自动添加认证头
|
||||
_playWithHeaders(song.url!);
|
||||
// ⭐ 关键:等待播放器加载完成
|
||||
await _playWithHeaders(song.url!);
|
||||
|
||||
// ⭐ 等待播放真正开始
|
||||
await _waitForPlaybackStarted();
|
||||
|
||||
_syncPlayerStateDelayed();
|
||||
|
||||
// ⭐ 只有真正开始播放后才推送通知
|
||||
if (_hasStartedCurrentPlayback) {
|
||||
_onSongChanged?.call(song);
|
||||
_loadMetadataForCurrentSong(generation);
|
||||
}
|
||||
} finally {
|
||||
_isChangingTrack = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// ⭐ 等待播放器真正开始播放
|
||||
Future<void> _waitForPlaybackStarted() async {
|
||||
final player = PlaybackService().player;
|
||||
|
||||
// 如果已经在播放,直接标记
|
||||
if (player.state.playing) {
|
||||
_hasStartedCurrentPlayback = true;
|
||||
debugPrint('🎵 [AudioService] playback already started');
|
||||
return;
|
||||
}
|
||||
|
||||
// 等待 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
|
||||
Future<void> _playWithHeaders(String url) async {
|
||||
@@ -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<void> 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<void> 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;
|
||||
|
||||
Reference in New Issue
Block a user