Enhance video playback features by adding playTime and initialPosition handling, and update PlayerControls for better focus management

This commit is contained in:
zimplexing
2025-07-07 22:14:56 +08:00
parent 08e24dd748
commit bd22fa2996
8 changed files with 94 additions and 55 deletions

View File

@@ -8,6 +8,7 @@ export type RowItem = (SearchResult | PlayRecord) & {
title: string;
poster: string;
progress?: number;
play_time?: number;
lastPlayed?: number;
episodeIndex?: number;
sourceName?: string;
@@ -61,48 +62,48 @@ const useHomeStore = create<HomeState>((set, get) => ({
error: null,
fetchInitialData: async () => {
const { selectedCategory } = get();
set({ loading: true, contentData: [], pageStart: 0, hasMore: true, error: null });
await get().loadMoreData();
set({ loading: false });
},
loadMoreData: async () => {
const { selectedCategory, pageStart, loading, loadingMore, hasMore } = get();
if (loading || loadingMore || !hasMore) return;
const { selectedCategory, pageStart, loadingMore, hasMore } = get();
if (loadingMore || !hasMore) return;
if (selectedCategory.type === 'record') {
const records = await PlayRecordManager.getAll();
const rowItems = Object.entries(records)
.map(([key, record]) => {
const [source, id] = key.split('+');
return { ...record, id, source, progress: record.play_time / record.total_time, poster: record.cover, sourceName: record.source_name, episodeIndex: record.index, totalEpisodes: record.total_episodes, lastPlayed: record.save_time };
})
.filter(record => record.progress !== undefined && record.progress > 0 && record.progress < 1)
.sort((a, b) => (b.lastPlayed || 0) - (a.lastPlayed || 0));
set({ contentData: rowItems, hasMore: false });
return;
if (pageStart > 0) {
set({ loadingMore: true });
}
if (!selectedCategory.type || !selectedCategory.tag) return;
set({ loadingMore: true });
try {
const result = await api.getDoubanData(selectedCategory.type, selectedCategory.tag, 20, pageStart);
if (result.list.length === 0) {
set({ hasMore: false });
if (selectedCategory.type === 'record') {
const records = await PlayRecordManager.getAll();
const rowItems = Object.entries(records)
.map(([key, record]) => {
const [source, id] = key.split('+');
return { ...record, id, source, progress: record.play_time / record.total_time, poster: record.cover, sourceName: record.source_name, episodeIndex: record.index, totalEpisodes: record.total_episodes, lastPlayed: record.save_time, play_time: record.play_time };
})
.filter(record => record.progress !== undefined && record.progress > 0 && record.progress < 1)
.sort((a, b) => (b.lastPlayed || 0) - (a.lastPlayed || 0));
set({ contentData: rowItems, hasMore: false });
} else if (selectedCategory.type && selectedCategory.tag) {
const result = await api.getDoubanData(selectedCategory.type, selectedCategory.tag, 20, pageStart);
if (result.list.length === 0) {
set({ hasMore: false });
} else {
const newItems = result.list.map(item => ({
...item,
id: item.title,
source: 'douban',
})) as RowItem[];
set(state => ({
contentData: pageStart === 0 ? newItems : [...state.contentData, ...newItems],
pageStart: state.pageStart + result.list.length,
hasMore: true,
}));
}
} else {
const newItems = result.list.map(item => ({
...item,
id: item.title,
source: 'douban',
})) as RowItem[];
set(state => ({
contentData: pageStart === 0 ? newItems : [...state.contentData, ...newItems],
pageStart: state.pageStart + result.list.length,
hasMore: true,
}));
set({ hasMore: false });
}
} catch (err: any) {
if (err.message === 'API_URL_NOT_SET') {
@@ -111,7 +112,7 @@ const useHomeStore = create<HomeState>((set, get) => ({
set({ error: '加载失败,请重试' });
}
} finally {
set({ loadingMore: false });
set({ loading: false, loadingMore: false });
}
},

View File

@@ -27,8 +27,9 @@ interface PlayerState {
isSeeking: boolean;
seekPosition: number;
progressPosition: number;
initialPosition: number;
setVideoRef: (ref: RefObject<Video>) => void;
loadVideo: (source: string, id: string, episodeIndex: number) => Promise<void>;
loadVideo: (source: string, id: string, episodeIndex: number, position?: number) => Promise<void>;
playEpisode: (index: number) => void;
togglePlayPause: () => void;
seek: (forward: boolean) => void;
@@ -53,11 +54,18 @@ const usePlayerStore = create<PlayerState>((set, get) => ({
isSeeking: false,
seekPosition: 0,
progressPosition: 0,
initialPosition: 0,
setVideoRef: (ref) => set({ videoRef: ref }),
loadVideo: async (source, id, episodeIndex) => {
set({ isLoading: true, detail: null, episodes: [], currentEpisodeIndex: 0 });
loadVideo: async (source, id, episodeIndex, position) => {
set({
isLoading: true,
detail: null,
episodes: [],
currentEpisodeIndex: 0,
initialPosition: position || 0,
});
try {
const videoDetail = await api.getVideoDetail(source, id);
const episodes = videoDetail.episodes.map((ep, index) => ({ url: ep, title: `${index + 1}` }));
@@ -76,7 +84,7 @@ const usePlayerStore = create<PlayerState>((set, get) => ({
playEpisode: (index) => {
const { episodes, videoRef } = get();
if (index >= 0 && index < episodes.length) {
set({ currentEpisodeIndex: index, showNextEpisodeOverlay: false });
set({ currentEpisodeIndex: index, showNextEpisodeOverlay: false, initialPosition: 0 });
videoRef?.current?.replayAsync();
}
},
@@ -156,6 +164,7 @@ const usePlayerStore = create<PlayerState>((set, get) => ({
showControls: true,
showEpisodeModal: false,
showNextEpisodeOverlay: false,
initialPosition: 0,
});
},
}));

View File

@@ -1,6 +1,7 @@
import { create } from 'zustand';
import { SettingsManager } from '@/services/storage';
import { api } from '@/services/api';
import useHomeStore from './homeStore';
interface SettingsState {
apiBaseUrl: string;
@@ -26,6 +27,7 @@ export const useSettingsStore = create<SettingsState>((set, get) => ({
await SettingsManager.save({ apiBaseUrl });
api.setBaseUrl(apiBaseUrl);
set({ isModalVisible: false });
useHomeStore.getState().fetchInitialData();
},
showModal: () => set({ isModalVisible: true }),
hideModal: () => set({ isModalVisible: false }),