fix(player): 解锁状态登录期内共享,重进播放页不再丢解锁
解锁记录原本是播放页实例的私有集合,退出再进入会先闪一次锁定态,还会 被 selectLocked 提前释放播放器。改为进程内共享的 UnlockedEpisodeRegistry, 由全局注入并在登录态变化时清空;播放器三处渲染分支统一走同一判定, 会话锁态只约束当前页,避免相邻预加载页被误判为锁定。
This commit is contained in:
1 parent
a4d08e7c1e
commit
57f84c45bd
6 files changed
+103
-18
No files matched your search
@@ -27,6 +27,7 @@ import '../../core/data/services/platform_share_service.dart';
|
||||
import '../../core/data/services/playback_position_cache.dart';
|
||||
import '../../core/data/services/playback_queue.dart';
|
||||
import '../../core/data/services/playback_session.dart';
|
||||
import '../../core/data/services/unlocked_episode_registry.dart';
|
||||
import '../../core/data/services/video_player_port.dart';
|
||||
import '../../core/data/services/wallet_controller.dart';
|
||||
import '../../core/formatting/localized_content.dart';
|
||||
@@ -64,11 +65,13 @@ class PlayerLogic extends ChangeNotifier {
|
||||
required this.pictureInPicture,
|
||||
this.wallet,
|
||||
this.downloads,
|
||||
UnlockedEpisodeRegistry? unlockedEpisodes,
|
||||
PlaybackPositionCache? positionCache,
|
||||
Future<void> Function()? waitForFrame,
|
||||
DateTime Function()? clock,
|
||||
bool Function()? isSignedIn,
|
||||
}) : positionCache = positionCache ?? PlaybackPositionCache(),
|
||||
}) : unlockedEpisodes = unlockedEpisodes ?? UnlockedEpisodeRegistry(),
|
||||
positionCache = positionCache ?? PlaybackPositionCache(),
|
||||
_waitForFrame =
|
||||
waitForFrame ?? (() => WidgetsBinding.instance.endOfFrame),
|
||||
_clock = clock ?? DateTime.now,
|
||||
@@ -79,6 +82,9 @@ class PlayerLogic extends ChangeNotifier {
|
||||
final AndroidPictureInPicture pictureInPicture;
|
||||
final WalletController? wallet;
|
||||
final DownloadController? downloads;
|
||||
|
||||
/// 已确认可播的收费集(登录期内共享):退出播放页再进来不必重新挡一次。
|
||||
final UnlockedEpisodeRegistry unlockedEpisodes;
|
||||
final PlaybackPositionCache positionCache;
|
||||
final Future<void> Function() _waitForFrame;
|
||||
final DateTime Function() _clock;
|
||||
@@ -169,14 +175,15 @@ class PlayerLogic extends ChangeNotifier {
|
||||
|
||||
/// 当前设备是否为 iOS 真机环境(面板据此显示自动小窗开关)。
|
||||
bool get iosDevice => !kIsWeb && Platform.isIOS;
|
||||
|
||||
/// PiP 状态是全局单通道回调,须广播给所有存活的 PlayerLogic:
|
||||
/// 推荐页每页一个 logic,若只保留最后一次注册的 handler,
|
||||
/// 相邻预加载页会盖掉正在播放页的注册,导致小窗 UI 不隐藏且无声音。
|
||||
static final Set<PlayerLogic> _pipSubscribers = <PlayerLogic>{};
|
||||
|
||||
/// 「记住上次选集」:dramaId → 集号。只在用户从选集面板主动挑集时写入
|
||||
/// (见 [_recordPickedEpisode]),feed 里同一部剧据此显示上次选中的集。
|
||||
static final Map<String, int> pickedEpisode = {};
|
||||
final Set<String> _unlockedEpisodeIds = {};
|
||||
final Map<String, EpisodeStats> _episodeStats = {};
|
||||
final Map<String, DateTime> _episodeStatsFetchedAt = {};
|
||||
final Map<String, Future<void>> _episodeStatsInFlight = {};
|
||||
@@ -562,7 +569,7 @@ class PlayerLogic extends ChangeNotifier {
|
||||
final localPath = await downloads?.findLocalPath(episode.id);
|
||||
if (request != pageRequest) return false;
|
||||
if (localPath != null) {
|
||||
_unlockedEpisodeIds.add(episode.id);
|
||||
unlockedEpisodes.markUnlocked(episode.id);
|
||||
blockedTicket = null;
|
||||
blockedEpisodeId = null;
|
||||
qualities = const [];
|
||||
@@ -587,7 +594,7 @@ class PlayerLogic extends ChangeNotifier {
|
||||
final playUrl = preferred ?? ticket.playUrl;
|
||||
if (preferred == null) selectedQuality = 'AUTO';
|
||||
if (ticket.accessible && playUrl.isNotEmpty) {
|
||||
_unlockedEpisodeIds.add(episode.id);
|
||||
unlockedEpisodes.markUnlocked(episode.id);
|
||||
// 签名地址原样写回,不做任何拼接/改写(AppConfig.mediaUrl 只补 host)
|
||||
sessionEpisodes[index] = episode.withVideoUrl(
|
||||
playUrl,
|
||||
@@ -860,7 +867,7 @@ class PlayerLogic extends ChangeNotifier {
|
||||
unlockError = service.unlockError;
|
||||
return false;
|
||||
}
|
||||
_unlockedEpisodeIds.add(episode.id);
|
||||
unlockedEpisodes.markUnlocked(episode.id);
|
||||
final ready = await ensureTicket(index, forceRefresh: true);
|
||||
if (ready) {
|
||||
await _selectVisible(index);
|
||||
@@ -1688,7 +1695,16 @@ class PlayerLogic extends ChangeNotifier {
|
||||
/// 收费集只有在本会话确认可播或解锁成功后才解除门禁。
|
||||
bool isEpisodeLocked(Episode episode) {
|
||||
return (episode.locked || !episode.isFree) &&
|
||||
!_unlockedEpisodeIds.contains(episode.id);
|
||||
!unlockedEpisodes.contains(episode.id);
|
||||
}
|
||||
|
||||
/// 单页锁定判定:剧集自身锁定 + 会话锁态(只约束当前页)+ 后端拒绝。
|
||||
///
|
||||
/// 播放页的多个渲染分支都必须走这里,否则相邻预加载页会被会话锁态误判成锁定。
|
||||
bool isEpisodeBlockedFor(Episode episode, int index) {
|
||||
return isEpisodeLocked(episode) ||
|
||||
(index == session.currentIndex && session.isLocked) ||
|
||||
blockedEpisodeId == episode.id;
|
||||
}
|
||||
|
||||
/// 按可见队列选择一集,并预热上一集与下一集。
|
||||
|
||||
Reference in new issue
Block a user