fix(metadata): 元数据回退到文件名时,在 80% 处重试一次
云服日志显示:对 /dav/.../*.flac 的 Range GET 在 10.3s 后返回 502, 是反代等待上游首包超时(清远源站响应慢),与我方 15s 超时无关。 影响仅限该曲的标签/封面,播放不受影响,但会一直回退文件名。 - _loadMetadataForCurrentSong: source == 'filename' 时置 _metadataNeedsRetry - 播放到 80% 且该曲未重试过 → 再抓一次(每首歌最多一次) - 因"播放未稳定"被跳过抓取的情况也标记为待重试 - 换歌时清空标记
This commit is contained in:
@@ -109,6 +109,10 @@ class AudioService extends ChangeNotifier {
|
|||||||
DateTime? _trackStartAt;
|
DateTime? _trackStartAt;
|
||||||
DateTime? _playReadyAt;
|
DateTime? _playReadyAt;
|
||||||
int _prefetchedGeneration = -1;
|
int _prefetchedGeneration = -1;
|
||||||
|
|
||||||
|
/// 元数据回退到文件名时置位,播到 80% 再试一次
|
||||||
|
bool _metadataNeedsRetry = false;
|
||||||
|
int _metadataRetryGeneration = -1;
|
||||||
bool _firstAudioReported = false;
|
bool _firstAudioReported = false;
|
||||||
|
|
||||||
/// 最近一次播放失败的原因,供 UI 提示;成功播放后自动清空。
|
/// 最近一次播放失败的原因,供 UI 提示;成功播放后自动清空。
|
||||||
@@ -319,6 +323,7 @@ class AudioService extends ChangeNotifier {
|
|||||||
_trackStartAt = DateTime.now();
|
_trackStartAt = DateTime.now();
|
||||||
_playReadyAt = null;
|
_playReadyAt = null;
|
||||||
_firstAudioReported = false;
|
_firstAudioReported = false;
|
||||||
|
_metadataNeedsRetry = false;
|
||||||
await _playWithHeaders(song.url!);
|
await _playWithHeaders(song.url!);
|
||||||
_playReadyAt = DateTime.now();
|
_playReadyAt = DateTime.now();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -411,6 +416,7 @@ class AudioService extends ChangeNotifier {
|
|||||||
final settled = await _waitForPlaybackSettled(epoch);
|
final settled = await _waitForPlaybackSettled(epoch);
|
||||||
if (!settled || epoch != _switchEpoch) {
|
if (!settled || epoch != _switchEpoch) {
|
||||||
debugPrint('⏸️ [AudioService] 播放未稳定,跳过本次元数据抓取(避免抢带宽)');
|
debugPrint('⏸️ [AudioService] 播放未稳定,跳过本次元数据抓取(避免抢带宽)');
|
||||||
|
_metadataNeedsRetry = true; // 留到 80% 再试
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _loadMetadataForCurrentSong(generation);
|
await _loadMetadataForCurrentSong(generation);
|
||||||
@@ -420,6 +426,25 @@ class AudioService extends ChangeNotifier {
|
|||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 播到 80% 时,对"元数据回退到文件名"的歌再抓一次。
|
||||||
|
///
|
||||||
|
/// 原因:反代在等上游首包超时时会直接回 502(实测 10.3s),
|
||||||
|
/// 起播阶段抓取很容易撞上;到 80% 时链路通常已经热了,重试成功率更高。
|
||||||
|
/// 每首歌最多重试一次。
|
||||||
|
void _maybeRetryMetadata(Duration position) {
|
||||||
|
if (!_metadataNeedsRetry) return;
|
||||||
|
|
||||||
|
final duration = durationNotifier.value;
|
||||||
|
if (duration.inMilliseconds <= 0) return;
|
||||||
|
if (position.inMilliseconds < duration.inMilliseconds * 0.8) return;
|
||||||
|
if (_metadataRetryGeneration == _playbackGeneration) return;
|
||||||
|
|
||||||
|
_metadataRetryGeneration = _playbackGeneration;
|
||||||
|
_metadataNeedsRetry = false;
|
||||||
|
debugPrint('🔁 [AudioService] 播放到 80%,重试一次元数据抓取');
|
||||||
|
_loadMetadataForCurrentSong(_playbackGeneration);
|
||||||
|
}
|
||||||
|
|
||||||
/// 播到 60% 时预热下一首(HEAD + Range 0-0,不下载正文)。
|
/// 播到 60% 时预热下一首(HEAD + Range 0-0,不下载正文)。
|
||||||
///
|
///
|
||||||
/// 日志实测起播延迟 2~3s 都在"等首批数据",提前完成 DNS/TLS/反代握手
|
/// 日志实测起播延迟 2~3s 都在"等首批数据",提前完成 DNS/TLS/反代握手
|
||||||
@@ -654,6 +679,15 @@ class AudioService extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.isNotEmpty) {
|
if (metadata.isNotEmpty) {
|
||||||
|
// 来源是文件名 = 元数据没读到(常见于反代 502 / 超时)
|
||||||
|
// 记下来,播到 80% 时再试一次,避免这首歌永远只有文件名
|
||||||
|
if (metadata.source == 'filename') {
|
||||||
|
_metadataNeedsRetry = true;
|
||||||
|
debugPrint('⚠️ [AudioService] 元数据回退到文件名,将在 80% 处重试一次');
|
||||||
|
} else {
|
||||||
|
_metadataNeedsRetry = false;
|
||||||
|
}
|
||||||
|
|
||||||
final updatedSong = Song(
|
final updatedSong = Song(
|
||||||
id: song.id,
|
id: song.id,
|
||||||
title: metadata.title.isNotEmpty ? metadata.title : song.title,
|
title: metadata.title.isNotEmpty ? metadata.title : song.title,
|
||||||
@@ -896,6 +930,7 @@ class AudioService extends ChangeNotifier {
|
|||||||
_playReadyAt = null;
|
_playReadyAt = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_maybeRetryMetadata(position);
|
||||||
_maybePrefetchNext(position);
|
_maybePrefetchNext(position);
|
||||||
positionNotifier.value = position;
|
positionNotifier.value = position;
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user