This commit is contained in:
lz_db
2026-10-25 16:14:26 +08:00
parent 7fae7387f5
commit 1d9cd1f9c7
10 changed files with 288 additions and 13 deletions
+42 -1
View File
@@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/config/test_stream.dart';
import '../core/network/api_exception.dart';
import '../core/player/feed_player_pool.dart';
import '../core/storage/feed_progress_store.dart';
import '../core/storage/watch_dedup_store.dart';
import '../models/video_item.dart';
import '../services/video_service.dart';
@@ -134,13 +135,27 @@ class FeedNotifier extends StateNotifier<FeedState> {
state = state.copyWith(isRefreshing: true, loadFailed: false);
try {
final page = await VideoService.fetchFeed(type: strategy.apiValue);
// 冷启动续播:按上次观看的 videoId 在新列表中定位(列表内容可能变化);
// 找不到时回退保存的下标(仅当仍在当前页范围内)
final saved = FeedProgressStore.I.read(strategy.name);
var index = 0;
if (saved != null && page.items.isNotEmpty) {
final hit = page.items.indexWhere((v) => v.videoId == saved.videoId);
if (hit >= 0) {
index = hit;
} else if (saved.index < page.items.length) {
index = saved.index;
}
pool.setResumePoint(saved.videoId, Duration(milliseconds: saved.positionMs));
}
state = FeedState(
items: page.items,
nextCursor: page.nextCursor,
currentIndex: index,
freeRemainingToday: state.freeRemainingToday,
);
if (page.items.isNotEmpty) {
_bindAndReport(0);
_bindAndReport(index);
}
} catch (_) {
state = state.copyWith(isRefreshing: false, loadFailed: true);
@@ -192,15 +207,41 @@ class FeedNotifier extends StateNotifier<FeedState> {
/// 翻页:滑出即暂停、滑入自动播放并上报观看计费(《01-开发文档》5.3)
void onPageChanged(int index) {
if (index < 0 || index >= state.items.length) return;
final item = state.items[index];
// 同一视频重新绑定(冷启动跳页)时保留播放位置,否则新页从 0 开始
final sameVideo = pool.currentVideoId == item.videoId;
// 立即静音全部非目标槽位,防止翻页动画期间多个视频同时出声
pool.pauseExcept(slotIndex: pool.slotOf(index));
state = state.copyWith(currentIndex: index);
// fire-and-forget:Future 内部会完整 await bindWindow 再 play,调用方不等
_bindAndReport(index);
// 持久化进度(杀进程重进时续播)
unawaited(FeedProgressStore.I.save(
strategy.name,
videoId: item.videoId,
index: index,
positionMs: sameVideo ? pool.currentPosition.inMilliseconds : 0,
));
// 接近尾部预取下一页
if (index >= state.items.length - 3) loadMore();
}
/// 持久化当前播放进度(App 退后台 / 切走 Tab / 定时兜底调用)
Future<void> persistProgress() async {
final index = state.currentIndex;
if (index < 0 || index >= state.items.length) return;
final item = state.items[index];
final positionMs =
pool.currentVideoId == item.videoId ? pool.currentPosition.inMilliseconds : 0;
await FeedProgressStore.I.save(
strategy.name,
videoId: item.videoId,
index: index,
positionMs: positionMs,
);
}
/// 绑定播放窗口 + 观看计费上报
Future<void> _bindAndReport(int index) async {
if (index < 0 || index >= state.items.length) return;