1
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user