fix(player): 使用高清首帧并稳定切集占位
This commit is contained in:
1 parent
0cb0ab0613
commit
2fef7470e0
10 files changed
+258
-33
No files matched your search
@@ -127,6 +127,7 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
DateTime? _lastDoubleTapAt;
|
||||
DateTime? _swipeStartedAt;
|
||||
bool _swipePrewarmCaptured = false;
|
||||
final Set<String> _precachedPosterUrls = {};
|
||||
|
||||
/// 收藏切换后的本地增量:基准是详情快照,切换成功按服务端状态 ±1。
|
||||
final Map<String, int> _favoriteDelta = {};
|
||||
@@ -376,7 +377,7 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
void _prewarmAdjacentThumbnails() {
|
||||
void _prewarmAdjacentFrames() {
|
||||
final current = logic.session.currentIndex;
|
||||
final indices = <int?>[
|
||||
current,
|
||||
@@ -387,9 +388,24 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
if (index == null || index < 0 || index >= logic.sessionEpisodes.length) {
|
||||
continue;
|
||||
}
|
||||
final thumbnailsUrl = logic.sessionEpisodes[index].thumbnailsUrl;
|
||||
if (thumbnailsUrl != null &&
|
||||
!logic.isEpisodeLocked(logic.sessionEpisodes[index])) {
|
||||
final episode = logic.sessionEpisodes[index];
|
||||
if (logic.isEpisodeLocked(episode)) continue;
|
||||
final posterUrl = episode.posterUrl?.trim() ?? '';
|
||||
if (posterUrl.isNotEmpty) {
|
||||
final imageUrl = AppConfig.mediaUrl(posterUrl);
|
||||
if (_precachedPosterUrls.add(imageUrl)) {
|
||||
unawaited(
|
||||
precacheImage(NetworkImage(imageUrl), context).catchError((
|
||||
Object _,
|
||||
) {
|
||||
_precachedPosterUrls.remove(imageUrl);
|
||||
}),
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
final thumbnailsUrl = episode.thumbnailsUrl;
|
||||
if (thumbnailsUrl != null && thumbnailsUrl.isNotEmpty) {
|
||||
unawaited(_spriteAxisService.prepareAt(thumbnailsUrl, Duration.zero));
|
||||
}
|
||||
}
|
||||
@@ -477,7 +493,7 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
final content = AnimatedBuilder(
|
||||
animation: logic.session,
|
||||
builder: (context, _) {
|
||||
_prewarmAdjacentThumbnails();
|
||||
_prewarmAdjacentFrames();
|
||||
final pages = NotificationListener<ScrollNotification>(
|
||||
onNotification: _handlePageScroll,
|
||||
child: PageView.builder(
|
||||
@@ -592,12 +608,17 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
final displayed = widget.homeMode
|
||||
? logic.displayEpisode(drama, sessionEpisode)
|
||||
: sessionEpisode;
|
||||
final episode =
|
||||
final needsSessionThumbnails =
|
||||
(displayed.thumbnailsUrl == null || displayed.thumbnailsUrl!.isEmpty) &&
|
||||
sessionEpisode.thumbnailsUrl != null
|
||||
sessionEpisode.thumbnailsUrl != null;
|
||||
final needsSessionPoster =
|
||||
(displayed.posterUrl == null || displayed.posterUrl!.isEmpty) &&
|
||||
sessionEpisode.posterUrl != null;
|
||||
final episode = needsSessionThumbnails || needsSessionPoster
|
||||
? displayed.withVideoUrl(
|
||||
displayed.videoUrl,
|
||||
thumbnailsUrl: sessionEpisode.thumbnailsUrl,
|
||||
posterUrl: sessionEpisode.posterUrl,
|
||||
)
|
||||
: displayed;
|
||||
final selected = index == logic.session.currentIndex;
|
||||
@@ -621,12 +642,19 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
final theater = logic.pip || logic.pipPreparing;
|
||||
final videoFit = theater ? BoxFit.contain : BoxFit.cover;
|
||||
final preparedFrameAvailable =
|
||||
selected && !locked && logic.session.reusedWarmAt(index);
|
||||
!locked &&
|
||||
videoReady &&
|
||||
(selected
|
||||
? logic.session.reusedWarmAt(index)
|
||||
: logic.session.preparedPlayerAt(index) != null);
|
||||
final warmSlotReused = logic.session.reusedWarmAt(index);
|
||||
final thumbnailsUrl = episode.thumbnailsUrl;
|
||||
final posterUrl = episode.posterUrl;
|
||||
final posterReady = posterUrl?.trim().isNotEmpty ?? false;
|
||||
final openingFrameReady =
|
||||
thumbnailsUrl != null &&
|
||||
_spriteAxisService.cachedFrameAt(thumbnailsUrl, Duration.zero) != null;
|
||||
if (thumbnailsUrl != null) {
|
||||
if (!posterReady && thumbnailsUrl != null) {
|
||||
unawaited(_spriteAxisService.prepareAt(thumbnailsUrl, Duration.zero));
|
||||
}
|
||||
if (selected &&
|
||||
@@ -654,7 +682,15 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
videoUrl: videoPort?.controller.dataSource ?? episode.videoUrl,
|
||||
theater: theater,
|
||||
warmFrameAvailable: preparedFrameAvailable,
|
||||
warmSlotReused: warmSlotReused,
|
||||
thumbnailReady: openingFrameReady,
|
||||
posterReady: posterReady,
|
||||
fallback: handoffPort != null
|
||||
? PlayerOpeningFallback.handoff
|
||||
: playerOpeningFallback(
|
||||
posterUrl: posterUrl,
|
||||
thumbnailsUrl: thumbnailsUrl,
|
||||
),
|
||||
waitForPlaybackFrame: logic.session.userWantsPlay,
|
||||
placeholder: locked
|
||||
? null
|
||||
@@ -665,6 +701,7 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
)
|
||||
: playerOpeningFrame(
|
||||
thumbnailsUrl,
|
||||
posterUrl: posterUrl,
|
||||
spriteSource: _spriteAxisService,
|
||||
fit: videoFit,
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user