fix(player): 分离首页与播放页播放上下文

This commit is contained in:
Zhou Weidong committed 2026-10-26 16:37:37 +08:00
1 parent 440188f261
commit 6667330ce0
8 files changed
+312 -362

No files matched your search

@@ -69,6 +69,7 @@ Future<void> _placeNeighbor(PlaybackSession session, _PlayerSlot? slot) async {
/// 当前集已经出画后,按手势方向静默准备上一集和下一集。
void _warmNeighbors(PlaybackSession session, {bool preferredNext = true}) {
if (!session.warmNeighborsEnabled) return;
final request = session._generation;
final nextTask = _warmOne(session, request, session._nextIndex, next: true);
final previousTask = _warmOne(
@@ -4,9 +4,6 @@ import '../models/drama.dart';
/// A player session has one navigation meaning: browse dramas or browse episodes.
class PlaybackQueue {
PlaybackQueue.fromEntries(List<PlaybackEntry> entries)
: entries = List.unmodifiable(entries);
PlaybackQueue.home(List<Drama> dramas)
: entries = List.unmodifiable(
dramas
+10 -44
View File
@@ -27,6 +27,7 @@ class PlaybackSession extends ChangeNotifier {
this.activate,
PlaybackCoordinator? coordinator,
this.waitForFrame,
this.warmNeighborsEnabled = true,
}) : _coordinator = coordinator ?? PlaybackCoordinator();
final List<Episode> episodes;
@@ -39,6 +40,7 @@ class PlaybackSession extends ChangeNotifier {
/// Runs only when a slot becomes current (e.g. progress listeners).
final PlayerPreparation? activate;
final Future<void> Function()? waitForFrame;
final bool warmNeighborsEnabled;
final PlaybackCoordinator _coordinator;
final Set<_PlayerSlot> _live = {};
_PlayerSlot? _active;
@@ -80,6 +82,8 @@ class PlaybackSession extends ChangeNotifier {
String? get error => _error;
// Includes initialization and asynchronous disposal, until native release ends.
int get livePlayerCount => _live.length;
bool get activeSlotActivated => _active?.activated ?? false;
bool get activeSlotPlaying => _active?.playing ?? false;
int? get preloadedIndex => _next?.prepared == true ? _next?.index : null;
int? get previousPreloadedIndex =>
_previous?.prepared == true ? _previous?.index : null;
@@ -137,50 +141,9 @@ class PlaybackSession extends ChangeNotifier {
if (_visible && !_overlayOpen) _warmNeighbors(this);
}
/// 在剧维度和集维度之间切换队列,但保留当前 active controller 与播放状态。
void remapQueuePreservingActive(
List<Episode> nextEpisodes, {
required int currentIndex,
int? preloadIndex,
int? previousIndex,
bool notify = true,
}) {
if (_closed ||
currentIndex < 0 ||
currentIndex >= nextEpisodes.length ||
_active == null) {
return;
}
_generation++;
episodes
..clear()
..addAll(nextEpisodes);
_active!.index = currentIndex;
_currentIndex = currentIndex;
_selected = true;
_loading = false;
_error = null;
_rememberNeighbors(preloadIndex, previousIndex);
final staleNext = _next;
final stalePrevious = _previous;
final staleHandoff = _handoff;
_next = null;
_previous = null;
_handoff = null;
if (notify) _notify();
unawaited(
_enqueue(() async {
await _retire(staleNext);
await _retire(stalePrevious);
await _retire(staleHandoff);
}),
);
if (_visible && !_overlayOpen) _warmNeighbors(this);
}
void _rememberNeighbors(int? preloadIndex, int? previousIndex) {
_nextIndex = preloadIndex;
_previousIndex = previousIndex;
_nextIndex = warmNeighborsEnabled ? preloadIndex : null;
_previousIndex = warmNeighborsEnabled ? previousIndex : null;
}
Future<void> _enqueue(Future<void> Function() operation) {
@@ -247,10 +210,13 @@ class PlaybackSession extends ChangeNotifier {
int index, {
int? preloadIndex,
int? previousIndex,
bool? userWantsPlay,
}) async {
if (_closed || index < 0 || index >= episodes.length) return;
// 切换到不同剧集时恢复播放;仍停留在同一集时保留用户暂停。
if (!_selected || index != _currentIndex) {
if (userWantsPlay != null) {
_userWantsPlay = userWantsPlay;
} else if (!_selected || index != _currentIndex) {
_userWantsPlay = true;
}
final request = ++_generation;
@@ -6,10 +6,71 @@ part of 'playback_session.dart';
extension PlaybackSessionControls on PlaybackSession {
/// 滑动手势开始时按方向优先准备相邻集,不改变当前集播放状态。
void prewarmNeighbors({required bool forward}) {
if (!_visible || _overlayOpen || _active?.prepared != true) return;
if (!warmNeighborsEnabled ||
!_visible ||
_overlayOpen ||
_active?.prepared != true) {
return;
}
_warmNeighbors(this, preferredNext: forward);
}
/// 路由交接时同步暂停当前原生播放器,controller 仍由 session 持有。
Future<void> pauseForRouteHandoff() {
if (_closed) return Future<void>.value();
_routeVisible = false;
return _enqueue(() async {
final slot = _active;
if (slot == null) return;
await slot.player.pause();
slot.playing = false;
});
}
/// 路由恢复可见后立即同步当前 controller,并等待原生播放状态落地。
Future<void> resumeForRouteHandoff() {
if (_closed) return Future<void>.value();
_routeVisible = true;
return _enqueue(() async {
final slot = _active;
if (kDebugMode) {
debugPrint(
'[feed-handoff] phase=resume slot=${slot?.index} '
'activated=${slot?.activated} playing=${slot?.playing} '
'retiring=${slot?.retiring} current=$_currentIndex '
'visible=$_visible user=$_userWantsPlay loading=$_loading',
);
}
if (slot == null || slot.retiring) return;
if (!slot.activated) {
try {
await activate?.call(episodes[slot.index], slot.player);
} catch (error) {
if (kDebugMode) {
debugPrint('[feed-handoff] phase=activation-failed error=$error');
}
}
if (_closed || _active != slot || slot.retiring) {
if (kDebugMode) {
debugPrint(
'[feed-handoff] phase=activation-aborted closed=$_closed '
'activeSame=${identical(_active, slot)} retiring=${slot.retiring}',
);
}
return;
}
slot.activated = true;
}
await _applyPlayback(slot);
if (kDebugMode) {
debugPrint(
'[feed-handoff] phase=resumed activated=${slot.activated} '
'playing=${slot.playing}',
);
}
});
}
/// 选中锁定集时立即停播并释放所有原生播放器,不创建新片源。
Future<void> selectLocked(
int index, {