perf(player): 续看点加载与进度写盘改为并行且复用同一请求

进播放页与首次准备播放会各要一次续看点,同一次请求跑两遍还串在首帧前;
现在并发请求复用同一 Future,并与上一条进度写盘并行等待,少一个网络往返的起播延迟。
剧场网格海报按布局宽度限制解码尺寸,半屏卡片不再按原图解码。
This commit is contained in:
Vicky authored and trae-solo-agent committed 2026-10-29 11:25:47 +08:00
1 parent 0f777b19f2
commit 63191b4692
3 files changed
+51 -12

No files matched your search

+22 -9
View File
@@ -7,6 +7,7 @@ import '../../../core/data/models/drama.dart';
import '../../../core/formatting/count_format.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_dimens.dart';
import '../../../core/widgets/remote_image.dart';
import '../../../l10n/app_localizations.dart';
const int _columns = 2;
@@ -218,16 +219,28 @@ class _DiscoverCoverImage extends StatelessWidget {
Widget build(BuildContext context) {
final url = AppConfig.mediaUrl(path);
if (url.isEmpty) return _placeholder();
return Image.network(
url,
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
loadingBuilder: (context, child, progress) {
if (progress == null) return child;
return _placeholder();
return LayoutBuilder(
builder: (context, constraints) {
// 卡片只有半屏宽,按原图解码一张竖版海报就是数 MB,按布局宽度限一下。
final decodeWidth = constraints.hasBoundedWidth
? resolveImageDecodePixels(
constraints.maxWidth,
MediaQuery.devicePixelRatioOf(context),
)
: null;
return Image.network(
url,
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
cacheWidth: decodeWidth,
loadingBuilder: (context, child, progress) {
if (progress == null) return child;
return _placeholder();
},
errorBuilder: (_, _, _) => _placeholder(),
);
},
errorBuilder: (_, _, _) => _placeholder(),
);
}
+17 -3
View File
@@ -110,6 +110,9 @@ class PlayerLogic extends ChangeNotifier {
ResumePoint? resumePoint;
bool resumeLoaded = false;
/// 续看点加载中的请求:进页与首次准备播放会同时触发,复用同一次请求。
Future<void>? _resumePointLoad;
/// 票据三态:加载中 / 失败(可重试)/ 被后端判定为不可播放
bool ticketLoading = false;
String? ticketError;
@@ -512,7 +515,15 @@ class PlayerLogic extends ChangeNotifier {
}
/// 服务端续看位置:进播放页时取一次,用于"定位到上次那一集的那一秒"。
Future<void> loadResumePoint() async {
Future<void> loadResumePoint() {
final inFlight = _resumePointLoad;
if (inFlight != null) return inFlight;
return _resumePointLoad = _loadResumePoint().whenComplete(() {
_resumePointLoad = null;
});
}
Future<void> _loadResumePoint() async {
final entry = queue.entryAt(initialIndex);
final cached = positionCache.read(
entry.episode.id,
@@ -1281,9 +1292,12 @@ class PlayerLogic extends ChangeNotifier {
await _waitUntilFrameReady(player.controller);
return;
}
await pendingWrite;
// 上一条进度写盘与续看点拉取互不依赖,并行等,少一个网络往返的首帧延迟。
// 服务端每剧只记一条:只有"记录里的那一集"才定位,其它集从头播
if (resumePoint == null) await loadResumePoint();
await Future.wait([
pendingWrite,
if (!resumeLoaded) loadResumePoint(),
]);
if (resumePositionFor(episode.id) == null) {
final dramaId = _dramaIdForEpisode(episode.id);
final cached = dramaId == null