fix(player): 修复详情往返播放位置丢失

This commit is contained in:
Zhou Weidong committed 2026-10-22 10:27:58 +08:00
1 parent fe49c6d8b2
commit 759a02375a
16 files changed
+464 -5

No files matched your search

@@ -4,6 +4,7 @@ import '../../core/data/models/drama.dart';
import '../../core/data/repositories/playback_repository.dart';
import '../../core/data/repositories/watch_history_repository.dart';
import '../../core/data/services/favorite_controller.dart';
import '../../core/data/services/playback_position_cache.dart';
import 'drama_detail_logic.dart';
class DramaDetailBinding {
@@ -12,10 +13,12 @@ class DramaDetailBinding {
required WatchHistoryRepository history,
required PlaybackRepository playback,
required FavoriteController favorites,
required PlaybackPositionCache positionCache,
}) => DramaDetailLogic(
drama: drama,
history: history,
playback: playback,
favorites: favorites,
positionCache: positionCache,
);
}
@@ -11,6 +11,7 @@ import '../../core/data/repositories/watch_history_repository.dart';
import '../../core/data/repositories/playback_repository.dart';
import '../../core/data/services/favorite_controller.dart';
import '../../core/data/services/download_controller.dart';
import '../../core/data/services/playback_position_cache.dart';
import '../../core/data/services/wallet_controller.dart';
import '../player/player_view.dart';
@@ -20,6 +21,7 @@ class DramaDetailLogic extends ChangeNotifier {
required this.history,
required this.playback,
required this.favorites,
required this.positionCache,
});
/// 先用列表卡片渲染,取到详情后换成带完整剧集的版本。
@@ -27,6 +29,7 @@ class DramaDetailLogic extends ChangeNotifier {
final WatchHistoryRepository history;
final PlaybackRepository playback;
final FavoriteController favorites;
final PlaybackPositionCache positionCache;
bool loading = false;
bool failed = false;
@@ -71,6 +74,23 @@ class DramaDetailLogic extends ChangeNotifier {
}
Future<void> loadContinue() async {
final cached = positionCache.latestForDrama(drama.id);
if (cached != null) {
final index = _indexOf(cached.episodeId);
if (index != null) {
progress = WatchProgress(
dramaId: cached.dramaId,
episodeId: cached.episodeId,
position: cached.position,
duration: cached.duration,
updatedAt: cached.updatedAt,
);
continueIndex = index;
progressError = null;
notify();
return;
}
}
try {
final record = await history.progressFor(drama.id);
if (_disposed) return;
@@ -12,6 +12,7 @@ import '../../core/data/repositories/watch_history_repository.dart';
import '../../core/data/services/catalog_controller.dart';
import '../../core/data/services/favorite_controller.dart';
import '../../core/data/services/interaction_controller.dart';
import '../../core/data/services/playback_position_cache.dart';
import '../../core/data/services/platform_share_service.dart';
import '../../core/formatting/error_text.dart';
import '../../core/formatting/localized_content.dart';
@@ -64,6 +65,7 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
history: context.read<WatchHistoryRepository>(),
playback: context.read<PlaybackRepository>(),
favorites: context.read<FavoriteController>(),
positionCache: context.read<PlaybackPositionCache>(),
);
_load();
}
+3
View File
@@ -4,6 +4,7 @@ import '../../core/data/repositories/watch_history_repository.dart';
import '../../core/data/repositories/playback_repository.dart';
import '../../core/data/services/android_picture_in_picture.dart';
import '../../core/data/services/download_controller.dart';
import '../../core/data/services/playback_position_cache.dart';
import '../../core/data/services/wallet_controller.dart';
import 'player_logic.dart';
@@ -11,11 +12,13 @@ class PlayerBinding {
PlayerLogic create({
required WatchHistoryRepository history,
required PlaybackRepository playback,
required PlaybackPositionCache positionCache,
WalletController? wallet,
DownloadController? downloads,
}) => PlayerLogic(
history: history,
playback: playback,
positionCache: positionCache,
pictureInPicture: AndroidPictureInPicture(),
wallet: wallet,
downloads: downloads,
+52 -3
View File
@@ -17,6 +17,7 @@ import '../../core/data/services/android_picture_in_picture.dart';
import '../../core/data/services/catalog_controller.dart';
import '../../core/data/services/download_controller.dart';
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/video_player_port.dart';
@@ -40,8 +41,10 @@ class PlayerLogic extends ChangeNotifier {
required this.pictureInPicture,
this.wallet,
this.downloads,
PlaybackPositionCache? positionCache,
Future<void> Function()? waitForFrame,
}) : _waitForFrame =
}) : positionCache = positionCache ?? PlaybackPositionCache(),
_waitForFrame =
waitForFrame ?? (() => WidgetsBinding.instance.endOfFrame);
final WatchHistoryRepository history;
@@ -49,6 +52,7 @@ class PlayerLogic extends ChangeNotifier {
final AndroidPictureInPicture pictureInPicture;
final WalletController? wallet;
final DownloadController? downloads;
final PlaybackPositionCache positionCache;
final Future<void> Function() _waitForFrame;
late PageController pages;
@@ -351,9 +355,22 @@ class PlayerLogic extends ChangeNotifier {
/// 服务端续看位置:进播放页时取一次,用于"定位到上次那一集的那一秒"。
Future<void> loadResumePoint() async {
final dramaId = queue.entryAt(initialIndex).drama.id;
final entry = queue.entryAt(initialIndex);
final cached = positionCache.read(
entry.episode.id,
dramaId: entry.drama.id,
);
if (cached != null) {
resumePoint = ResumePoint(
episodeId: cached.episodeId,
position: cached.position,
);
resumeLoaded = true;
if (isMounted()) notify();
return;
}
try {
resumePoint = await history.resumePointFor(dramaId);
resumePoint = await history.resumePointFor(entry.drama.id);
} catch (_) {
// 拿不到续看位置不影响播放:从当前集开头看
resumePoint = null;
@@ -639,6 +656,18 @@ class PlayerLogic extends ChangeNotifier {
await pendingWrite;
// 服务端每剧只记一条:只有"记录里的那一集"才定位,其它集从头播
if (resumePoint == null) await loadResumePoint();
if (resumePositionFor(episode.id) == null) {
final dramaId = _dramaIdForEpisode(episode.id);
final cached = dramaId == null
? null
: positionCache.read(episode.id, dramaId: dramaId);
if (cached != null) {
resumePoint = ResumePoint(
episodeId: cached.episodeId,
position: cached.position,
);
}
}
final saved = resumePositionFor(episode.id);
if (saved != null) {
await player.controller.seekTo(
@@ -667,6 +696,12 @@ class PlayerLogic extends ChangeNotifier {
if (session.active != player || !player.controller.value.isInitialized) {
return;
}
positionCache.save(
episodeId: episode.id,
dramaId: drama.id,
position: player.controller.value.position,
duration: player.controller.value.duration,
);
if (player.controller.value.isCompleted && !completionProgressSaved) {
completionProgressSaved = true;
unawaited(
@@ -750,6 +785,12 @@ class PlayerLogic extends ChangeNotifier {
!player.controller.value.isInitialized) {
return pendingWrite;
}
positionCache.save(
episodeId: episode.id,
dramaId: drama.id,
position: player.controller.value.position,
duration: player.controller.value.duration,
);
return queueProgress(
drama,
episode,
@@ -851,6 +892,7 @@ class PlayerLogic extends ChangeNotifier {
Future<void> openDetail(BuildContext context) async {
session.setOverlayOpen(true);
await saveCurrentProgress();
try {
if (!context.mounted) return;
await Navigator.of(context).push(
@@ -865,6 +907,13 @@ class PlayerLogic extends ChangeNotifier {
await saveCurrentProgress();
}
String? _dramaIdForEpisode(String episodeId) {
for (final entry in queue.entries) {
if (entry.episode.id == episodeId) return entry.drama.id;
}
return null;
}
Future<void> openSeriesAt(
BuildContext context,
Drama drama,
+2
View File
@@ -11,6 +11,7 @@ import '../../core/data/repositories/comment_repository.dart';
import '../../core/data/services/favorite_controller.dart';
import '../../core/data/services/download_controller.dart';
import '../../core/data/services/interaction_controller.dart';
import '../../core/data/services/playback_position_cache.dart';
import '../../core/data/services/playback_session.dart';
import '../../core/data/services/video_player_port.dart';
import '../../core/data/services/wallet_controller.dart';
@@ -119,6 +120,7 @@ class _PlayerPageState extends State<PlayerPage>
logic = PlayerBinding().create(
history: context.read<WatchHistoryRepository>(),
playback: context.read<PlaybackRepository>(),
positionCache: context.read<PlaybackPositionCache>(),
wallet: widget.wallet,
downloads: widget.downloads,
);