From daba1649983ec3f48477824f09935ef0a51b3fba Mon Sep 17 00:00:00 2001 From: zimplexing Date: Wed, 16 Jul 2025 16:36:46 +0800 Subject: [PATCH] refactor: update storage management to use centralized storage configuration and improve README documentation --- README.md | 39 +++------------------------------------ services/storage.ts | 13 ++++++------- services/storageConfig.ts | 20 ++++++++++++++++++++ stores/playerStore.ts | 12 ++++++------ stores/settingsStore.ts | 21 ++++++++++++--------- 5 files changed, 47 insertions(+), 58 deletions(-) create mode 100644 services/storageConfig.ts diff --git a/README.md b/README.md index 23e6c7b..31c60aa 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,6 @@ - [Expo Router](https://docs.expo.dev/router/introduction/) - [Expo AV](https://docs.expo.dev/versions/latest/sdk/av/) - TypeScript -- **后端**: - - [Node.js](https://nodejs.org/) - - [Express](https://expressjs.com/) - - [TypeScript](https://www.typescriptlang.org/) ## 📂 项目结构 @@ -31,7 +27,6 @@ . ├── app/ # Expo Router 路由和页面 ├── assets/ # 静态资源 (字体, 图片, TV 图标) -├── backend/ # 后端 Express 应用 ├── components/ # React 组件 ├── constants/ # 应用常量 (颜色, 样式) ├── hooks/ # 自定义 Hooks @@ -52,24 +47,7 @@ - [Xcode](https://developer.apple.com/xcode/) (用于 Apple TV 开发) - [Android Studio](https://developer.android.com/studio) (用于 Android TV 开发) -### 1. 后端服务 - -首先,启动后端服务: - -```sh -# 进入后端目录 -cd backend - -# 安装依赖 -yarn - -# 启动开发服务器 -yarn dev -``` - -后端服务将运行在 `http://localhost:3001`。 - -### 2. 前端应用 +### 项目启动 接下来,在项目根目录运行前端应用: @@ -93,19 +71,9 @@ yarn android-tv ## 部署 -### 后端部署 +推荐使用 [MoonTV](https://github.com/senshinya/MoonTV) 部署,地址可直接使用部署后的访问地址。 -#### [Vercel](https://vercel.com/) 部署 - -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fzimplexing%2FOrionTV&root-directory=backend) - -如果 vercel 部署后遇到 `404: NOT_FOUNDCode: NOT_FOUND`,请检查 vercel 项目的根目录是否是 `beackend` - -#### Docker 部署 - -1. `docker pull zimpel1/tv-host` - -2. `docker run -d -p 3001:3001 zimpel1/tv-host` +如果不想依赖 MoonTV,可以使用 1.1.x 版本。 ## 其他 @@ -153,4 +121,3 @@ OrionTV 仅作为视频搜索工具,不存储、上传或分发任何视频内 - [gpt-load](https://github.com/tbphp/gpt-load) - 一个高性能的 OpenAI 格式 API 多密钥轮询代理服务器,支持负载均衡,使用 Go 语言开发 - [one-balance](https://github.com/glidea/one-balance) - Make ai KEY rotation SMARTER and more SECURE - diff --git a/services/storage.ts b/services/storage.ts index cdf6da4..46442dd 100644 --- a/services/storage.ts +++ b/services/storage.ts @@ -1,6 +1,6 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { api, PlayRecord as ApiPlayRecord, Favorite as ApiFavorite } from "./api"; -import { useSettingsStore } from "@/stores/settingsStore"; +import { storageConfig } from "./storageConfig"; // --- Storage Keys --- const STORAGE_KEYS = { @@ -83,7 +83,7 @@ export class PlayerSettingsManager { // --- FavoriteManager (Dynamic: API or LocalStorage) --- export class FavoriteManager { private static getStorageType() { - return useSettingsStore.getState().serverConfig?.StorageType; + return storageConfig.getStorageType(); } static async getAll(): Promise> { @@ -154,7 +154,7 @@ export class FavoriteManager { // --- PlayRecordManager (Dynamic: API or LocalStorage) --- export class PlayRecordManager { private static getStorageType() { - return useSettingsStore.getState().serverConfig?.StorageType; + return storageConfig.getStorageType(); } static async getAll(): Promise> { @@ -232,7 +232,7 @@ export class PlayRecordManager { // --- SearchHistoryManager (Dynamic: API or LocalStorage) --- export class SearchHistoryManager { private static getStorageType() { - return useSettingsStore.getState().serverConfig?.StorageType; + return storageConfig.getStorageType(); } static async get(): Promise { @@ -270,7 +270,7 @@ export class SearchHistoryManager { } } -// --- SettingsManager (Remains unchanged, uses AsyncStorage) --- +// --- SettingsManager (Uses AsyncStorage) --- export class SettingsManager { static async get(): Promise { const defaultSettings: AppSettings = { @@ -280,8 +280,7 @@ export class SettingsManager { enabledAll: true, sources: {}, }, - m3uUrl: - "", + m3uUrl: "", }; try { const data = await AsyncStorage.getItem(STORAGE_KEYS.SETTINGS); diff --git a/services/storageConfig.ts b/services/storageConfig.ts new file mode 100644 index 0000000..4b97b03 --- /dev/null +++ b/services/storageConfig.ts @@ -0,0 +1,20 @@ +// Define a simple storage configuration service +export interface StorageConfig { + storageType: string | undefined; + getStorageType: () => string | undefined; + setStorageType: (type: string | undefined) => void; +} + +// Create a singleton instance +export const storageConfig: StorageConfig = { + // Default to undefined (will fallback to local storage) + storageType: undefined, + + getStorageType() { + return this.storageType; + }, + + setStorageType(type: string | undefined) { + this.storageType = type; + }, +}; diff --git a/stores/playerStore.ts b/stores/playerStore.ts index 9e3619e..f7003bb 100644 --- a/stores/playerStore.ts +++ b/stores/playerStore.ts @@ -73,18 +73,18 @@ const usePlayerStore = create((set, get) => ({ set({ isLoading: true, - currentEpisodeIndex: episodeIndex, - initialPosition: position || 0, - episodes: episodes.map((ep, index) => ({ - url: ep, - title: `第 ${index + 1} 集`, - })), }); try { const playRecord = await PlayRecordManager.get(detail.source, detail.id.toString()); set({ isLoading: false, + currentEpisodeIndex: episodeIndex, + initialPosition: position || 0, + episodes: episodes.map((ep, index) => ({ + url: ep, + title: `第 ${index + 1} 集`, + })), introEndTime: playRecord?.introEndTime, outroStartTime: playRecord?.outroStartTime, }); diff --git a/stores/settingsStore.ts b/stores/settingsStore.ts index 12afaf5..d4c4336 100644 --- a/stores/settingsStore.ts +++ b/stores/settingsStore.ts @@ -1,9 +1,9 @@ -import { create } from 'zustand'; -import { SettingsManager } from '@/services/storage'; -import { api, ServerConfig } from '@/services/api'; +import { create } from "zustand"; +import { SettingsManager } from "@/services/storage"; +import { api, ServerConfig } from "@/services/api"; +import { storageConfig } from "@/services/storageConfig"; // import useHomeStore from './homeStore'; - interface SettingsState { apiBaseUrl: string; m3uUrl: string; @@ -22,14 +22,14 @@ interface SettingsState { setM3uUrl: (url: string) => void; setRemoteInputEnabled: (enabled: boolean) => void; saveSettings: () => Promise; - setVideoSource: (config: { enabledAll: boolean; sources: {[key: string]: boolean} }) => void; + setVideoSource: (config: { enabledAll: boolean; sources: { [key: string]: boolean } }) => void; showModal: () => void; hideModal: () => void; } export const useSettingsStore = create((set, get) => ({ - apiBaseUrl: '', - m3uUrl: '', + apiBaseUrl: "", + m3uUrl: "", liveStreamSources: [], remoteInputEnabled: false, isModalVisible: false, @@ -55,6 +55,9 @@ export const useSettingsStore = create((set, get) => ({ fetchServerConfig: async () => { try { const config = await api.getServerConfig(); + if (config) { + storageConfig.setStorageType(config.StorageType); + } set({ serverConfig: config }); } catch (error) { console.info("Failed to fetch server config:", error); @@ -66,7 +69,7 @@ export const useSettingsStore = create((set, get) => ({ setVideoSource: (config) => set({ videoSource: config }), saveSettings: async () => { const { apiBaseUrl, m3uUrl, remoteInputEnabled, videoSource } = get(); - await SettingsManager.save({ + await SettingsManager.save({ apiBaseUrl, m3uUrl, remoteInputEnabled, @@ -78,4 +81,4 @@ export const useSettingsStore = create((set, get) => ({ }, showModal: () => set({ isModalVisible: true }), hideModal: () => set({ isModalVisible: false }), -})); \ No newline at end of file +}));