日志实测:open 完成仅 37~60ms,但起播耗时 2.1~2.8s, 说明等待发生在 mpv 拿首批数据,而不是建连。 - 新增 PrefetchService:HEAD + Range 0-0,只走 DNS/TLS/反代, 不下载正文;失败静默,不影响播放 - AudioService 在进度达到 60%(且时长 > 60s)时预热下一首, 每首歌只触发一次;shuffle 模式下按当前随机索引推算 待验证:播到 60% 时应出现 🔥 [AudioService] 预热下一首:xxx 🔥 [Prefetch] 预热完成 xxxms,status=200/206 对比下一首的 "起播耗时" 是否下降。
69 lines
1.9 KiB
Dart
69 lines
1.9 KiB
Dart
// lib/services/prefetch_service.dart
|
||
import 'package:dio/dio.dart';
|
||
import 'package:flutter/foundation.dart';
|
||
|
||
import 'webdav_service.dart';
|
||
|
||
/// 下一首预热。
|
||
///
|
||
/// 目的:在用户真正切歌之前,提前完成 DNS 解析、TLS 握手、反代/CDN 的首包,
|
||
/// 让 mpv 起播时少等一会儿。
|
||
///
|
||
/// 只发 HEAD + `Range: bytes=0-0`,不下载正文,带宽开销可以忽略;
|
||
/// 失败也完全静默,不影响当前播放。
|
||
class PrefetchService {
|
||
static final PrefetchService _instance = PrefetchService._internal();
|
||
factory PrefetchService() => _instance;
|
||
PrefetchService._internal();
|
||
|
||
final Dio _dio = Dio();
|
||
|
||
String? _lastUrl;
|
||
bool _busy = false;
|
||
|
||
Future<void> warm(String url) async {
|
||
if (url.isEmpty) return;
|
||
if (_busy) return;
|
||
if (_lastUrl == url) return;
|
||
|
||
_busy = true;
|
||
_lastUrl = url;
|
||
|
||
final sw = Stopwatch()..start();
|
||
try {
|
||
final headers = await WebDAVService.instance.getAuthHeaders();
|
||
if (headers.isEmpty) return;
|
||
|
||
final response = await _dio.head(
|
||
url,
|
||
options: Options(
|
||
headers: {
|
||
...headers,
|
||
'Range': 'bytes=0-0',
|
||
},
|
||
receiveTimeout: const Duration(seconds: 8),
|
||
sendTimeout: const Duration(seconds: 5),
|
||
// HEAD 返回 4xx/5xx 也不抛异常,交给日志记录
|
||
validateStatus: (_) => true,
|
||
),
|
||
);
|
||
|
||
debugPrint('🔥 [Prefetch] 预热完成 ${sw.elapsedMilliseconds}ms,'
|
||
'status=${response.statusCode}');
|
||
} catch (e) {
|
||
debugPrint('⚠️ [Prefetch] 预热失败(忽略): $e');
|
||
} finally {
|
||
_busy = false;
|
||
// 允许同一首歌在下一轮重新预热(比如重播)
|
||
if (_lastUrl == url) {
|
||
// 保留 _lastUrl 做去重,下一次换歌时会被覆盖
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 切歌/停止时清掉去重状态
|
||
void reset() {
|
||
_lastUrl = null;
|
||
}
|
||
}
|