mirror of
https://gh-proxy.org/https://github.com/tiajinsha/JKVideo
synced 2026-07-08 07:28:37 +08:00
bug修改
This commit is contained in:
30
store/miniExclusion.ts
Normal file
30
store/miniExclusion.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* MiniPlayer ↔ LiveMiniPlayer 互斥协调。
|
||||
* 通过 zustand subscribe 在外部串联两个 store,避免它们直接互相 import
|
||||
* 造成循环依赖(曾导致 Metro / TS 编译时内存暴涨)。
|
||||
*
|
||||
* 仅需在 app 启动时 import 一次(_layout.tsx)即可生效。
|
||||
*/
|
||||
import { useVideoStore } from './videoStore';
|
||||
import { useLiveStore } from './liveStore';
|
||||
|
||||
let initialized = false;
|
||||
|
||||
export function initMiniExclusion() {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
|
||||
// 视频 mini 激活 → 清掉直播 mini
|
||||
useVideoStore.subscribe((state, prev) => {
|
||||
if (state.isActive && !prev.isActive && useLiveStore.getState().isActive) {
|
||||
useLiveStore.getState().clearLive();
|
||||
}
|
||||
});
|
||||
|
||||
// 直播 mini 激活 → 清掉视频 mini
|
||||
useLiveStore.subscribe((state, prev) => {
|
||||
if (state.isActive && !prev.isActive && useVideoStore.getState().isActive) {
|
||||
useVideoStore.getState().clearVideo();
|
||||
}
|
||||
});
|
||||
}
|
||||
74
store/playProgressStore.ts
Normal file
74
store/playProgressStore.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { create } from 'zustand';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
const STORAGE_KEY = 'play_progress';
|
||||
// 持久化最多 200 条记录
|
||||
const MAX_ENTRIES = 200;
|
||||
// 视频接近结尾(>= 95%)视为看完,清掉记录
|
||||
const FINISH_RATIO = 0.95;
|
||||
|
||||
interface ProgressEntry {
|
||||
bvid: string;
|
||||
time: number; // 秒
|
||||
duration: number; // 秒
|
||||
ts: number; // 写入时间戳
|
||||
}
|
||||
|
||||
interface PlayProgressStore {
|
||||
records: Record<string, ProgressEntry>;
|
||||
hydrated: boolean;
|
||||
hydrate: () => Promise<void>;
|
||||
save: (bvid: string, time: number, duration: number) => void;
|
||||
get: (bvid: string) => number; // 返回应跳转的秒数(无记录返回 0)
|
||||
clear: (bvid: string) => void;
|
||||
}
|
||||
|
||||
const persist = (records: Record<string, ProgressEntry>) => {
|
||||
// 控制总量:超量删最早
|
||||
const entries = Object.values(records).sort((a, b) => b.ts - a.ts);
|
||||
const trimmed = entries.slice(0, MAX_ENTRIES);
|
||||
const trimmedMap: Record<string, ProgressEntry> = {};
|
||||
trimmed.forEach(e => { trimmedMap[e.bvid] = e; });
|
||||
AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(trimmedMap)).catch(() => {});
|
||||
return trimmedMap;
|
||||
};
|
||||
|
||||
export const usePlayProgressStore = create<PlayProgressStore>((set, get) => ({
|
||||
records: {},
|
||||
hydrated: false,
|
||||
hydrate: async () => {
|
||||
try {
|
||||
const raw = await AsyncStorage.getItem(STORAGE_KEY);
|
||||
if (raw) {
|
||||
const records = JSON.parse(raw);
|
||||
set({ records, hydrated: true });
|
||||
return;
|
||||
}
|
||||
} catch {}
|
||||
set({ hydrated: true });
|
||||
},
|
||||
save: (bvid, time, duration) => {
|
||||
if (!bvid || !duration || duration <= 0) return;
|
||||
// 视频接近结尾,清掉记录
|
||||
if (time / duration >= FINISH_RATIO) {
|
||||
get().clear(bvid);
|
||||
return;
|
||||
}
|
||||
// 起始 5 秒不必记录
|
||||
if (time < 5) return;
|
||||
const records = { ...get().records };
|
||||
records[bvid] = { bvid, time, duration, ts: Date.now() };
|
||||
set({ records: persist(records) });
|
||||
},
|
||||
get: (bvid) => {
|
||||
const r = get().records[bvid];
|
||||
return r?.time ?? 0;
|
||||
},
|
||||
clear: (bvid) => {
|
||||
const records = { ...get().records };
|
||||
if (records[bvid]) {
|
||||
delete records[bvid];
|
||||
set({ records: persist(records) });
|
||||
}
|
||||
},
|
||||
}));
|
||||
43
store/playUrlCache.ts
Normal file
43
store/playUrlCache.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { create } from 'zustand';
|
||||
import type { PlayUrlResponse } from '../services/types';
|
||||
|
||||
interface CacheEntry {
|
||||
playData: PlayUrlResponse;
|
||||
mpdUri?: string;
|
||||
ts: number;
|
||||
}
|
||||
|
||||
interface PlayUrlCacheStore {
|
||||
// 不通过 set 触发渲染(只是用 store 持有 Map)
|
||||
cache: Map<string, CacheEntry>;
|
||||
get: (bvid: string, qn: number) => CacheEntry | undefined;
|
||||
set: (bvid: string, qn: number, entry: Omit<CacheEntry, 'ts'>) => void;
|
||||
}
|
||||
|
||||
const TTL_MS = 5 * 60 * 1000;
|
||||
const MAX_ENTRIES = 30;
|
||||
|
||||
const makeKey = (bvid: string, qn: number) => `${bvid}_${qn}`;
|
||||
|
||||
export const usePlayUrlCache = create<PlayUrlCacheStore>((_set, getState) => ({
|
||||
cache: new Map(),
|
||||
get: (bvid, qn) => {
|
||||
const map = getState().cache;
|
||||
const entry = map.get(makeKey(bvid, qn));
|
||||
if (!entry) return undefined;
|
||||
if (Date.now() - entry.ts > TTL_MS) {
|
||||
map.delete(makeKey(bvid, qn));
|
||||
return undefined;
|
||||
}
|
||||
return entry;
|
||||
},
|
||||
set: (bvid, qn, entry) => {
|
||||
const map = getState().cache;
|
||||
map.set(makeKey(bvid, qn), { ...entry, ts: Date.now() });
|
||||
// 简单 LRU:超过容量删最早
|
||||
if (map.size > MAX_ENTRIES) {
|
||||
const oldestKey = map.keys().next().value;
|
||||
if (oldestKey) map.delete(oldestKey);
|
||||
}
|
||||
},
|
||||
}));
|
||||
11
store/visibleBigKeyStore.ts
Normal file
11
store/visibleBigKeyStore.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { create } from "zustand";
|
||||
|
||||
interface VisibleBigKeyState {
|
||||
key: string | null;
|
||||
setKey: (key: string | null) => void;
|
||||
}
|
||||
|
||||
export const useVisibleBigKeyStore = create<VisibleBigKeyState>((set) => ({
|
||||
key: null,
|
||||
setKey: (key) => set({ key }),
|
||||
}));
|
||||
Reference in New Issue
Block a user