feat(player): enhance video playback with SSL error fallback and performance optimizations

- Add comprehensive SSL certificate error detection and automatic source switching
- Implement smart video source fallback strategy with failed source tracking
- Enhance video component with optimized event handlers and useCallback patterns
- Add explicit playAsync() call in onLoad to improve auto-play reliability
- Integrate performance monitoring with detailed logging throughout playback chain
- Optimize Video component props with useMemo and custom useVideoHandlers hook
- Add source matching fixes for fallback scenarios in DetailStore
- Enhance error handling with user-friendly messages and recovery strategies
This commit is contained in:
zimplexing
2025-08-15 22:41:18 +08:00
parent 172815f926
commit 836285dbd5
7 changed files with 761 additions and 116 deletions

View File

@@ -204,7 +204,8 @@ export class API {
async searchVideo(query: string, resourceId: string, signal?: AbortSignal): Promise<{ results: SearchResult[] }> {
const url = `/api/search/one?q=${encodeURIComponent(query)}&resourceId=${encodeURIComponent(resourceId)}`;
const response = await this._fetch(url, { signal });
return response.json();
const { results } = await response.json();
return { results: results.filter((item: any) => item.title === query )};
}
async getResources(signal?: AbortSignal): Promise<ApiSite[]> {

View File

@@ -10,21 +10,33 @@ export const getResolutionFromM3U8 = async (
url: string,
signal?: AbortSignal
): Promise<string | null> => {
const perfStart = performance.now();
console.info(`[PERF] M3U8 resolution detection START - url: ${url.substring(0, 100)}...`);
// 1. Check cache first
const cachedEntry = resolutionCache[url];
if (cachedEntry && Date.now() - cachedEntry.timestamp < CACHE_DURATION) {
const perfEnd = performance.now();
console.info(`[PERF] M3U8 resolution detection CACHED - took ${(perfEnd - perfStart).toFixed(2)}ms, resolution: ${cachedEntry.resolution}`);
return cachedEntry.resolution;
}
if (!url.toLowerCase().endsWith(".m3u8")) {
console.info(`[PERF] M3U8 resolution detection SKIPPED - not M3U8 file`);
return null;
}
try {
const fetchStart = performance.now();
const response = await fetch(url, { signal });
const fetchEnd = performance.now();
console.info(`[PERF] M3U8 fetch took ${(fetchEnd - fetchStart).toFixed(2)}ms, status: ${response.status}`);
if (!response.ok) {
return null;
}
const parseStart = performance.now();
const playlist = await response.text();
const lines = playlist.split("\n");
let highestResolution = 0;
@@ -42,6 +54,9 @@ export const getResolutionFromM3U8 = async (
}
}
}
const parseEnd = performance.now();
console.info(`[PERF] M3U8 parsing took ${(parseEnd - parseStart).toFixed(2)}ms, lines: ${lines.length}`);
// 2. Store result in cache
resolutionCache[url] = {
@@ -49,8 +64,13 @@ export const getResolutionFromM3U8 = async (
timestamp: Date.now(),
};
const perfEnd = performance.now();
console.info(`[PERF] M3U8 resolution detection COMPLETE - took ${(perfEnd - perfStart).toFixed(2)}ms, resolution: ${resolutionString}`);
return resolutionString;
} catch {
} catch (error) {
const perfEnd = performance.now();
console.info(`[PERF] M3U8 resolution detection ERROR - took ${(perfEnd - perfStart).toFixed(2)}ms, error: ${error}`);
return null;
}
};

View File

@@ -59,8 +59,16 @@ export class PlayerSettingsManager {
}
static async get(source: string, id: string): Promise<PlayerSettings | null> {
const perfStart = performance.now();
console.info(`[PERF] PlayerSettingsManager.get START - source: ${source}, id: ${id}`);
const allSettings = await this.getAll();
return allSettings[generateKey(source, id)] || null;
const result = allSettings[generateKey(source, id)] || null;
const perfEnd = performance.now();
console.info(`[PERF] PlayerSettingsManager.get END - took ${(perfEnd - perfStart).toFixed(2)}ms, found: ${!!result}`);
return result;
}
static async save(source: string, id: string, settings: PlayerSettings): Promise<void> {
@@ -165,8 +173,12 @@ export class PlayRecordManager {
}
static async getAll(): Promise<Record<string, PlayRecord>> {
const perfStart = performance.now();
const storageType = this.getStorageType();
console.info(`[PERF] PlayRecordManager.getAll START - storageType: ${storageType}`);
let apiRecords: Record<string, PlayRecord> = {};
if (this.getStorageType() === "localstorage") {
if (storageType === "localstorage") {
try {
const data = await AsyncStorage.getItem(STORAGE_KEYS.PLAY_RECORDS);
apiRecords = data ? JSON.parse(data) : {};
@@ -175,7 +187,13 @@ export class PlayRecordManager {
return {};
}
} else {
const apiStart = performance.now();
console.info(`[PERF] API getPlayRecords START`);
apiRecords = await api.getPlayRecords();
const apiEnd = performance.now();
console.info(`[PERF] API getPlayRecords END - took ${(apiEnd - apiStart).toFixed(2)}ms, records: ${Object.keys(apiRecords).length}`);
}
const localSettings = await PlayerSettingsManager.getAll();
@@ -186,6 +204,10 @@ export class PlayRecordManager {
...localSettings[key],
};
}
const perfEnd = performance.now();
console.info(`[PERF] PlayRecordManager.getAll END - took ${(perfEnd - perfStart).toFixed(2)}ms, total records: ${Object.keys(mergedRecords).length}`);
return mergedRecords;
}
@@ -207,9 +229,18 @@ export class PlayRecordManager {
}
static async get(source: string, id: string): Promise<PlayRecord | null> {
const perfStart = performance.now();
const key = generateKey(source, id);
const storageType = this.getStorageType();
console.info(`[PERF] PlayRecordManager.get START - source: ${source}, id: ${id}, storageType: ${storageType}`);
const records = await this.getAll();
return records[key] || null;
const result = records[key] || null;
const perfEnd = performance.now();
console.info(`[PERF] PlayRecordManager.get END - took ${(perfEnd - perfStart).toFixed(2)}ms, found: ${!!result}`);
return result;
}
static async remove(source: string, id: string): Promise<void> {