mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-03-11 22:27:30 +08:00
refactor: update play time property and enhance player settings management
- Changed the play time property from 'play_time' to 'time' in HomeScreen. - Removed unused player control functions from PlayScreen. - Added PlayerSettings interface and implemented PlayerSettingsManager for local storage of player settings. - Refactored PlayRecordManager to merge API records with local player settings. - Updated authentication logic in authStore to handle optional username parameter in login function. - Cleaned up and optimized imports across various components.
This commit is contained in:
@@ -96,7 +96,7 @@ export default function HomeScreen() {
|
|||||||
year={item.year}
|
year={item.year}
|
||||||
rate={item.rate}
|
rate={item.rate}
|
||||||
progress={item.progress}
|
progress={item.progress}
|
||||||
playTime={item.play_time}
|
playTime={item.time}
|
||||||
episodeIndex={item.episodeIndex}
|
episodeIndex={item.episodeIndex}
|
||||||
sourceName={item.sourceName}
|
sourceName={item.sourceName}
|
||||||
totalEpisodes={item.totalEpisodes}
|
totalEpisodes={item.totalEpisodes}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export default function PlayScreen() {
|
|||||||
|
|
||||||
const { detail } = useDetailStore();
|
const { detail } = useDetailStore();
|
||||||
const {
|
const {
|
||||||
status,
|
|
||||||
isLoading,
|
isLoading,
|
||||||
showControls,
|
showControls,
|
||||||
showNextEpisodeOverlay,
|
showNextEpisodeOverlay,
|
||||||
@@ -37,9 +36,6 @@ export default function PlayScreen() {
|
|||||||
setShowControls,
|
setShowControls,
|
||||||
setShowNextEpisodeOverlay,
|
setShowNextEpisodeOverlay,
|
||||||
reset,
|
reset,
|
||||||
playEpisode,
|
|
||||||
togglePlayPause,
|
|
||||||
seek,
|
|
||||||
} = usePlayerStore();
|
} = usePlayerStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export interface PlayRecord {
|
|||||||
play_time: number;
|
play_time: number;
|
||||||
total_time: number;
|
total_time: number;
|
||||||
save_time: number;
|
save_time: number;
|
||||||
|
year: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiSite {
|
export interface ApiSite {
|
||||||
@@ -108,7 +109,7 @@ export class API {
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(username: string | undefined, password: string): Promise<{ ok: boolean }> {
|
async login(username?: string | undefined, password?: string): Promise<{ ok: boolean }> {
|
||||||
const response = await this._fetch("/api/login", {
|
const response = await this._fetch("/api/login", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { api, PlayRecord as ApiPlayRecord, Favorite as ApiFavorite } from "./api
|
|||||||
// --- Storage Keys ---
|
// --- Storage Keys ---
|
||||||
const STORAGE_KEYS = {
|
const STORAGE_KEYS = {
|
||||||
SETTINGS: "mytv_settings",
|
SETTINGS: "mytv_settings",
|
||||||
|
PLAYER_SETTINGS: "mytv_player_settings",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// --- Type Definitions (aligned with api.ts) ---
|
// --- Type Definitions (aligned with api.ts) ---
|
||||||
@@ -14,6 +15,11 @@ export type PlayRecord = ApiPlayRecord & {
|
|||||||
};
|
};
|
||||||
export type Favorite = ApiFavorite;
|
export type Favorite = ApiFavorite;
|
||||||
|
|
||||||
|
export interface PlayerSettings {
|
||||||
|
introEndTime?: number;
|
||||||
|
outroStartTime?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AppSettings {
|
export interface AppSettings {
|
||||||
apiBaseUrl: string;
|
apiBaseUrl: string;
|
||||||
remoteInputEnabled: boolean;
|
remoteInputEnabled: boolean;
|
||||||
@@ -29,6 +35,47 @@ export interface AppSettings {
|
|||||||
// --- Helper ---
|
// --- Helper ---
|
||||||
const generateKey = (source: string, id: string) => `${source}+${id}`;
|
const generateKey = (source: string, id: string) => `${source}+${id}`;
|
||||||
|
|
||||||
|
// --- PlayerSettingsManager (Uses AsyncStorage) ---
|
||||||
|
export class PlayerSettingsManager {
|
||||||
|
static async getAll(): Promise<Record<string, PlayerSettings>> {
|
||||||
|
try {
|
||||||
|
const data = await AsyncStorage.getItem(STORAGE_KEYS.PLAYER_SETTINGS);
|
||||||
|
return data ? JSON.parse(data) : {};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get all player settings:", error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async get(source: string, id: string): Promise<PlayerSettings | null> {
|
||||||
|
const allSettings = await this.getAll();
|
||||||
|
return allSettings[generateKey(source, id)] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async save(source: string, id: string, settings: PlayerSettings): Promise<void> {
|
||||||
|
const allSettings = await this.getAll();
|
||||||
|
const key = generateKey(source, id);
|
||||||
|
// Only save if there are actual values to save
|
||||||
|
if (settings.introEndTime !== undefined || settings.outroStartTime !== undefined) {
|
||||||
|
allSettings[key] = { ...allSettings[key], ...settings };
|
||||||
|
} else {
|
||||||
|
// If both are undefined, remove the key
|
||||||
|
delete allSettings[key];
|
||||||
|
}
|
||||||
|
await AsyncStorage.setItem(STORAGE_KEYS.PLAYER_SETTINGS, JSON.stringify(allSettings));
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(source: string, id: string): Promise<void> {
|
||||||
|
const allSettings = await this.getAll();
|
||||||
|
delete allSettings[generateKey(source, id)];
|
||||||
|
await AsyncStorage.setItem(STORAGE_KEYS.PLAYER_SETTINGS, JSON.stringify(allSettings));
|
||||||
|
}
|
||||||
|
|
||||||
|
static async clearAll(): Promise<void> {
|
||||||
|
await AsyncStorage.removeItem(STORAGE_KEYS.PLAYER_SETTINGS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- FavoriteManager (Refactored to use API) ---
|
// --- FavoriteManager (Refactored to use API) ---
|
||||||
export class FavoriteManager {
|
export class FavoriteManager {
|
||||||
static async getAll(): Promise<Record<string, Favorite>> {
|
static async getAll(): Promise<Record<string, Favorite>> {
|
||||||
@@ -67,30 +114,53 @@ export class FavoriteManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- PlayRecordManager (Refactored to use API) ---
|
// --- PlayRecordManager (Refactored to use API and local settings) ---
|
||||||
export class PlayRecordManager {
|
export class PlayRecordManager {
|
||||||
static async getAll(): Promise<Record<string, PlayRecord>> {
|
static async getAll(): Promise<Record<string, PlayRecord>> {
|
||||||
return (await api.getPlayRecords()) as Record<string, PlayRecord>;
|
const apiRecords = await api.getPlayRecords();
|
||||||
|
const localSettings = await PlayerSettingsManager.getAll();
|
||||||
|
|
||||||
|
const mergedRecords: Record<string, PlayRecord> = {};
|
||||||
|
for (const key in apiRecords) {
|
||||||
|
mergedRecords[key] = {
|
||||||
|
...apiRecords[key],
|
||||||
|
...localSettings[key],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return mergedRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async save(source: string, id: string, record: Omit<PlayRecord, "save_time">): Promise<void> {
|
static async save(source: string, id: string, record: Omit<PlayRecord, "save_time">): Promise<void> {
|
||||||
const key = generateKey(source, id);
|
const key = generateKey(source, id);
|
||||||
// The API will handle setting the save_time
|
const { introEndTime, outroStartTime, ...apiRecord } = record;
|
||||||
await api.savePlayRecord(key, record);
|
|
||||||
|
// Save player settings locally
|
||||||
|
await PlayerSettingsManager.save(source, id, { introEndTime, outroStartTime });
|
||||||
|
|
||||||
|
// Save core record to API
|
||||||
|
await api.savePlayRecord(key, apiRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async get(source: string, id: string): Promise<PlayRecord | null> {
|
static async get(source: string, id: string): Promise<PlayRecord | null> {
|
||||||
|
const key = generateKey(source, id);
|
||||||
|
// This can be optimized, but for consistency, we call getAll
|
||||||
const records = await this.getAll();
|
const records = await this.getAll();
|
||||||
return records[generateKey(source, id)] || null;
|
return records[key] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(source: string, id: string): Promise<void> {
|
static async remove(source: string, id: string): Promise<void> {
|
||||||
const key = generateKey(source, id);
|
const key = generateKey(source, id);
|
||||||
|
// Remove from API first
|
||||||
await api.deletePlayRecord(key);
|
await api.deletePlayRecord(key);
|
||||||
|
// Then remove from local settings
|
||||||
|
await PlayerSettingsManager.remove(source, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async clearAll(): Promise<void> {
|
static async clearAll(): Promise<void> {
|
||||||
|
// Clear from API first
|
||||||
await api.deletePlayRecord();
|
await api.deletePlayRecord();
|
||||||
|
// Then clear from local settings
|
||||||
|
await PlayerSettingsManager.clearAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ const useAuthStore = create<AuthState>((set) => ({
|
|||||||
hideLoginModal: () => set({ isLoginModalVisible: false }),
|
hideLoginModal: () => set({ isLoginModalVisible: false }),
|
||||||
checkLoginStatus: async () => {
|
checkLoginStatus: async () => {
|
||||||
try {
|
try {
|
||||||
|
const { ok } = await api.login();
|
||||||
|
if (ok) {
|
||||||
|
set({ isLoggedIn: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
const cookies = await Cookies.get(api.baseURL);
|
const cookies = await Cookies.get(api.baseURL);
|
||||||
const isLoggedIn = cookies && !!cookies.auth;
|
const isLoggedIn = cookies && !!cookies.auth;
|
||||||
set({ isLoggedIn });
|
set({ isLoggedIn });
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from "zustand";
|
||||||
import { api, SearchResult, PlayRecord } from '@/services/api';
|
import { api, SearchResult, PlayRecord } from "@/services/api";
|
||||||
import { PlayRecordManager } from '@/services/storage';
|
import { PlayRecordManager } from "@/services/storage";
|
||||||
import useAuthStore from './authStore';
|
import useAuthStore from "./authStore";
|
||||||
|
|
||||||
export type RowItem = (SearchResult | PlayRecord) & {
|
export type RowItem = (SearchResult | PlayRecord) & {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -20,18 +20,38 @@ export type RowItem = (SearchResult | PlayRecord) & {
|
|||||||
|
|
||||||
export interface Category {
|
export interface Category {
|
||||||
title: string;
|
title: string;
|
||||||
type?: 'movie' | 'tv' | 'record';
|
type?: "movie" | "tv" | "record";
|
||||||
tag?: string;
|
tag?: string;
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialCategories: Category[] = [
|
const initialCategories: Category[] = [
|
||||||
{ title: '最近播放', type: 'record' },
|
{ title: "最近播放", type: "record" },
|
||||||
{ title: '热门剧集', type: 'tv', tag: '热门' },
|
{ title: "热门剧集", type: "tv", tag: "热门" },
|
||||||
{ title: '电视剧', type: 'tv', tags: [ '国产剧', '美剧', '英剧', '韩剧', '日剧', '港剧', '日本动画', '动画'] },
|
{ title: "电视剧", type: "tv", tags: ["国产剧", "美剧", "英剧", "韩剧", "日剧", "港剧", "日本动画", "动画"] },
|
||||||
{ title: '电影', type: 'movie', tags: ['热门', '最新', '经典', '豆瓣高分', '冷门佳片', '华语', '欧美', '韩国', '日本', '动作', '喜剧', '爱情', '科幻', '悬疑', '恐怖'] },
|
{
|
||||||
{ title: '综艺', type: 'tv', tag: '综艺' },
|
title: "电影",
|
||||||
{ title: '豆瓣 Top250', type: 'movie', tag: 'top250' },
|
type: "movie",
|
||||||
|
tags: [
|
||||||
|
"热门",
|
||||||
|
"最新",
|
||||||
|
"经典",
|
||||||
|
"豆瓣高分",
|
||||||
|
"冷门佳片",
|
||||||
|
"华语",
|
||||||
|
"欧美",
|
||||||
|
"韩国",
|
||||||
|
"日本",
|
||||||
|
"动作",
|
||||||
|
"喜剧",
|
||||||
|
"爱情",
|
||||||
|
"科幻",
|
||||||
|
"悬疑",
|
||||||
|
"恐怖",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ title: "综艺", type: "tv", tag: "综艺" },
|
||||||
|
{ title: "豆瓣 Top250", type: "movie", tag: "top250" },
|
||||||
];
|
];
|
||||||
|
|
||||||
interface HomeState {
|
interface HomeState {
|
||||||
@@ -73,7 +93,7 @@ const useHomeStore = create<HomeState>((set, get) => ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (selectedCategory.type === 'record') {
|
if (selectedCategory.type === "record") {
|
||||||
const { isLoggedIn } = useAuthStore.getState();
|
const { isLoggedIn } = useAuthStore.getState();
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
set({ contentData: [], hasMore: false });
|
set({ contentData: [], hasMore: false });
|
||||||
@@ -82,24 +102,35 @@ const useHomeStore = create<HomeState>((set, get) => ({
|
|||||||
const records = await PlayRecordManager.getAll();
|
const records = await PlayRecordManager.getAll();
|
||||||
const rowItems = Object.entries(records)
|
const rowItems = Object.entries(records)
|
||||||
.map(([key, record]) => {
|
.map(([key, record]) => {
|
||||||
const [source, id] = key.split('+');
|
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 };
|
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)
|
.filter((record) => record.progress !== undefined && record.progress > 0 && record.progress < 1)
|
||||||
.sort((a, b) => (b.lastPlayed || 0) - (a.lastPlayed || 0));
|
.sort((a, b) => (b.lastPlayed || 0) - (a.lastPlayed || 0));
|
||||||
|
|
||||||
set({ contentData: rowItems, hasMore: false });
|
set({ contentData: rowItems, hasMore: false });
|
||||||
} else if (selectedCategory.type && selectedCategory.tag) {
|
} else if (selectedCategory.type && selectedCategory.tag) {
|
||||||
const result = await api.getDoubanData(selectedCategory.type, selectedCategory.tag, 20, pageStart);
|
const result = await api.getDoubanData(selectedCategory.type, selectedCategory.tag, 20, pageStart);
|
||||||
if (result.list.length === 0) {
|
if (result.list.length === 0) {
|
||||||
set({ hasMore: false });
|
set({ hasMore: false });
|
||||||
} else {
|
} else {
|
||||||
const newItems = result.list.map(item => ({
|
const newItems = result.list.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
id: item.title,
|
id: item.title,
|
||||||
source: 'douban',
|
source: "douban",
|
||||||
})) as RowItem[];
|
})) as RowItem[];
|
||||||
set(state => ({
|
set((state) => ({
|
||||||
contentData: pageStart === 0 ? newItems : [...state.contentData, ...newItems],
|
contentData: pageStart === 0 ? newItems : [...state.contentData, ...newItems],
|
||||||
pageStart: state.pageStart + result.list.length,
|
pageStart: state.pageStart + result.list.length,
|
||||||
hasMore: true,
|
hasMore: true,
|
||||||
@@ -112,10 +143,10 @@ const useHomeStore = create<HomeState>((set, get) => ({
|
|||||||
set({ hasMore: false });
|
set({ hasMore: false });
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err.message === 'API_URL_NOT_SET') {
|
if (err.message === "API_URL_NOT_SET") {
|
||||||
set({ error: '请点击右上角设置按钮,配置您的 API 地址' });
|
set({ error: "请点击右上角设置按钮,配置您的 API 地址" });
|
||||||
} else {
|
} else {
|
||||||
set({ error: '加载失败,请重试' });
|
set({ error: "加载失败,请重试" });
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
set({ loading: false, loadingMore: false });
|
set({ loading: false, loadingMore: false });
|
||||||
@@ -134,12 +165,12 @@ const useHomeStore = create<HomeState>((set, get) => ({
|
|||||||
refreshPlayRecords: async () => {
|
refreshPlayRecords: async () => {
|
||||||
const { isLoggedIn } = useAuthStore.getState();
|
const { isLoggedIn } = useAuthStore.getState();
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
set(state => {
|
set((state) => {
|
||||||
const recordCategoryExists = state.categories.some(c => c.type === 'record');
|
const recordCategoryExists = state.categories.some((c) => c.type === "record");
|
||||||
if (recordCategoryExists) {
|
if (recordCategoryExists) {
|
||||||
const newCategories = state.categories.filter(c => c.type !== 'record');
|
const newCategories = state.categories.filter((c) => c.type !== "record");
|
||||||
if (state.selectedCategory.type === 'record') {
|
if (state.selectedCategory.type === "record") {
|
||||||
get().selectCategory(newCategories[0] || null);
|
get().selectCategory(newCategories[0] || null);
|
||||||
}
|
}
|
||||||
return { categories: newCategories };
|
return { categories: newCategories };
|
||||||
}
|
}
|
||||||
@@ -149,24 +180,24 @@ const useHomeStore = create<HomeState>((set, get) => ({
|
|||||||
}
|
}
|
||||||
const records = await PlayRecordManager.getAll();
|
const records = await PlayRecordManager.getAll();
|
||||||
const hasRecords = Object.keys(records).length > 0;
|
const hasRecords = Object.keys(records).length > 0;
|
||||||
set(state => {
|
set((state) => {
|
||||||
const recordCategoryExists = state.categories.some(c => c.type === 'record');
|
const recordCategoryExists = state.categories.some((c) => c.type === "record");
|
||||||
if (hasRecords && !recordCategoryExists) {
|
if (hasRecords && !recordCategoryExists) {
|
||||||
return { categories: [initialCategories[0], ...state.categories] };
|
return { categories: [initialCategories[0], ...state.categories] };
|
||||||
}
|
}
|
||||||
if (!hasRecords && recordCategoryExists) {
|
if (!hasRecords && recordCategoryExists) {
|
||||||
const newCategories = state.categories.filter(c => c.type !== 'record');
|
const newCategories = state.categories.filter((c) => c.type !== "record");
|
||||||
if (state.selectedCategory.type === 'record') {
|
if (state.selectedCategory.type === "record") {
|
||||||
get().selectCategory(newCategories[0] || null);
|
get().selectCategory(newCategories[0] || null);
|
||||||
}
|
}
|
||||||
return { categories: newCategories };
|
return { categories: newCategories };
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
if (get().selectedCategory.type === 'record') {
|
if (get().selectedCategory.type === "record") {
|
||||||
get().fetchInitialData();
|
get().fetchInitialData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useHomeStore;
|
export default useHomeStore;
|
||||||
|
|||||||
@@ -27,11 +27,7 @@ interface PlayerState {
|
|||||||
introEndTime?: number;
|
introEndTime?: number;
|
||||||
outroStartTime?: number;
|
outroStartTime?: number;
|
||||||
setVideoRef: (ref: RefObject<Video>) => void;
|
setVideoRef: (ref: RefObject<Video>) => void;
|
||||||
loadVideo: (
|
loadVideo: (source: string, episodeIndex: number, position?: number) => Promise<void>;
|
||||||
source: string,
|
|
||||||
episodeIndex: number,
|
|
||||||
position?: number
|
|
||||||
) => Promise<void>;
|
|
||||||
playEpisode: (index: number) => void;
|
playEpisode: (index: number) => void;
|
||||||
togglePlayPause: () => void;
|
togglePlayPause: () => void;
|
||||||
seek: (duration: number) => void;
|
seek: (duration: number) => void;
|
||||||
@@ -86,10 +82,7 @@ const usePlayerStore = create<PlayerState>((set, get) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const playRecord = await PlayRecordManager.get(
|
const playRecord = await PlayRecordManager.get(detail.source, detail.id.toString());
|
||||||
detail.source,
|
|
||||||
detail.id.toString()
|
|
||||||
);
|
|
||||||
set({
|
set({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
introEndTime: playRecord?.introEndTime,
|
introEndTime: playRecord?.introEndTime,
|
||||||
@@ -101,7 +94,6 @@ const usePlayerStore = create<PlayerState>((set, get) => ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
playEpisode: (index) => {
|
playEpisode: (index) => {
|
||||||
const { episodes, videoRef } = get();
|
const { episodes, videoRef } = get();
|
||||||
if (index >= 0 && index < episodes.length) {
|
if (index >= 0 && index < episodes.length) {
|
||||||
@@ -209,12 +201,13 @@ const usePlayerStore = create<PlayerState>((set, get) => ({
|
|||||||
};
|
};
|
||||||
PlayRecordManager.save(detail.source, detail.id.toString(), {
|
PlayRecordManager.save(detail.source, detail.id.toString(), {
|
||||||
title: detail.title,
|
title: detail.title,
|
||||||
poster: detail.poster || "",
|
cover: detail.poster || "",
|
||||||
index: currentEpisodeIndex,
|
index: currentEpisodeIndex,
|
||||||
total_episodes: episodes.length,
|
total_episodes: episodes.length,
|
||||||
play_time: status.positionMillis,
|
play_time: status.positionMillis,
|
||||||
total_time: status.durationMillis || 0,
|
total_time: status.durationMillis || 0,
|
||||||
source_name: detail.source_name,
|
source_name: detail.source_name,
|
||||||
|
year: detail.year || "",
|
||||||
...existingRecord,
|
...existingRecord,
|
||||||
...updates,
|
...updates,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user