fix(player): 收紧图片与快速切集资源占用

This commit is contained in:
Zhou Weidong committed 2026-10-26 15:10:02 +08:00
1 parent 2fef7470e0
commit 80c962a95c
10 files changed
+281 -30

No files matched your search

+34
View File
@@ -93,6 +93,9 @@ class PlayerLogic extends ChangeNotifier {
String selectedQuality = 'AUTO';
Duration? _qualitySeek;
int pageRequest = 0;
int? _pendingSelectionIndex;
int? _pendingSelectionRevision;
Future<void>? _selectionDrain;
bool muted = false;
double playbackSpeed = 1.0;
bool stoppedAtFeedEnd = false;
@@ -957,6 +960,37 @@ class PlayerLogic extends ChangeNotifier {
);
}
/// 翻页入口使用 single-flight:执行中的切集不被重复排队,只保留最新目标。
Future<void> requestEpisodeSelection(int index, {int? revision}) {
if (_disposed || index < 0 || index >= sessionEpisodes.length) {
return Future<void>.value();
}
_pendingSelectionIndex = index;
_pendingSelectionRevision = revision;
return _selectionDrain ??= _drainEpisodeSelections();
}
Future<void> _drainEpisodeSelections() async {
try {
while (!_disposed) {
final index = _pendingSelectionIndex;
if (index == null) break;
final revision = _pendingSelectionRevision;
_pendingSelectionIndex = null;
_pendingSelectionRevision = null;
await selectEpisode(index, revision: revision);
}
} finally {
_selectionDrain = null;
final index = _pendingSelectionIndex;
if (!_disposed && index != null) {
unawaited(
requestEpisodeSelection(index, revision: _pendingSelectionRevision),
);
}
}
}
Future<void> selectEpisode(int index, {int? revision}) async {
final feedRevisionAtStart = revision ?? feedRevision;
final request = ++pageRequest;
+23 -1
View File
@@ -353,6 +353,28 @@ class _PlayerPageState extends State<PlayerPage>
);
}
Future<void> _selectPage(int index) async {
await logic.requestEpisodeSelection(index);
if (!mounted || logic.session.currentIndex != index) return;
await logic.session.preloadSettled;
if (!mounted || logic.session.currentIndex != index) return;
_logResourceWatermark();
}
void _logResourceWatermark() {
if (!kDebugMode) return;
final imageCache = PaintingBinding.instance.imageCache;
debugPrint(
'[player-resources] '
'slots=${logic.session.livePlayerCount} '
'warmNext=${logic.session.preloadedIndex ?? '-'} '
'warmPrevious=${logic.session.previousPreloadedIndex ?? '-'} '
'sprites=${_spriteAxisService.spriteCacheSize} '
'imageCacheBytes=${imageCache.currentSizeBytes} '
'imageCacheCount=${imageCache.currentSize}',
);
}
void _setScrubbing(bool value) {
if (!mounted) return;
if (_scrubbing == value) return;
@@ -507,7 +529,7 @@ class _PlayerPageState extends State<PlayerPage>
onPageChanged: (position) {
final index = indices[position];
widget.onDramaChanged?.call(logic.queue.entryAt(index).drama);
unawaited(logic.selectEpisode(index));
unawaited(_selectPage(index));
if (position >= indices.length - 3) {
final loadMore = widget.onLoadMore;
if (loadMore != null) unawaited(loadMore());
@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import '../../../core/config/app_config.dart';
import '../../../core/data/services/sprite_thumbnail_axis.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/widgets/remote_image.dart';
import 'scrub_preview_card.dart';
enum PlayerOpeningFallback { handoff, poster, thumbnails, black }
@@ -149,12 +150,25 @@ class _PlayerOpeningFrameState extends State<PlayerOpeningFrame> {
children: [
const ColoredBox(color: AppColors.mediaBlack),
if (_hasPoster)
Image.network(
AppConfig.mediaUrl(widget.posterUrl!.trim()),
key: const Key('player-opening-poster'),
fit: widget.fit,
gaplessPlayback: true,
errorBuilder: (_, _, _) => _handlePosterError(),
LayoutBuilder(
builder: (context, constraints) {
final pixelRatio = MediaQuery.devicePixelRatioOf(context);
return Image.network(
AppConfig.mediaUrl(widget.posterUrl!.trim()),
key: const Key('player-opening-poster'),
fit: widget.fit,
cacheWidth: resolveImageDecodePixels(
constraints.maxWidth,
pixelRatio,
),
cacheHeight: resolveImageDecodePixels(
constraints.maxHeight,
pixelRatio,
),
gaplessPlayback: true,
errorBuilder: (_, _, _) => _handlePosterError(),
);
},
)
else
_spriteOrBlack(),