fix(player): 分离首页与播放页播放上下文
This commit is contained in:
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
|
||||
|
||||
@@ -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, {
|
||||
|
||||
Reference in new issue
Block a user