| |
| |
| |
|
|
| import type { VideoSource, SourceSubscription } from '@/lib/types'; |
|
|
| |
| |
| |
| export interface ImportSourceFormat { |
| id: string; |
| name: string; |
| baseUrl: string; |
| group?: 'normal' | 'adult'; |
| enabled?: boolean; |
| priority?: number; |
| } |
|
|
| |
| |
| |
| export interface ImportResult { |
| normalSources: VideoSource[]; |
| adultSources: VideoSource[]; |
| totalCount: number; |
| } |
|
|
| |
| |
| |
| export function convertToVideoSource(source: ImportSourceFormat): VideoSource { |
| return { |
| id: source.id, |
| name: source.name, |
| baseUrl: source.baseUrl, |
| searchPath: '', |
| detailPath: '', |
| enabled: source.enabled !== false, |
| priority: source.priority || 1, |
| group: source.group || 'normal', |
| }; |
| } |
|
|
| |
| |
| |
| export function isValidSourceFormat(obj: unknown): obj is ImportSourceFormat { |
| if (typeof obj !== 'object' || obj === null) return false; |
| const source = obj as Record<string, unknown>; |
| return ( |
| typeof source.id === 'string' && |
| typeof source.name === 'string' && |
| typeof source.baseUrl === 'string' && |
| source.id.length > 0 && |
| source.name.length > 0 && |
| source.baseUrl.length > 0 |
| ); |
| } |
|
|
| |
| |
| |
| |
| export function parseSourcesFromJson(jsonString: string): ImportResult { |
| const data = JSON.parse(jsonString); |
|
|
| let sourcesArray: unknown[]; |
|
|
| |
| if (Array.isArray(data)) { |
| sourcesArray = data; |
| } else if (data.sources && Array.isArray(data.sources)) { |
| sourcesArray = data.sources; |
| } else if (data.list && Array.isArray(data.list)) { |
| sourcesArray = data.list; |
| } else { |
| throw new Error('无法识别的JSON格式'); |
| } |
|
|
| const normalSources: VideoSource[] = []; |
| const adultSources: VideoSource[] = []; |
|
|
| for (const item of sourcesArray) { |
| if (!isValidSourceFormat(item)) continue; |
|
|
| const source = convertToVideoSource(item); |
|
|
| if (item.group === 'adult') { |
| adultSources.push(source); |
| } else { |
| normalSources.push(source); |
| } |
| } |
|
|
| return { |
| normalSources, |
| adultSources, |
| totalCount: normalSources.length + adultSources.length, |
| }; |
| } |
|
|
| |
| |
| |
| export async function fetchSourcesFromUrl(url: string): Promise<ImportResult> { |
| |
| const isExternal = url.startsWith('http') && (typeof window !== 'undefined' && !url.includes(window.location.host)); |
| const fetchUrl = isExternal |
| ? `/api/proxy?url=${encodeURIComponent(url)}` |
| : url; |
|
|
| const response = await fetch(fetchUrl, { |
| headers: { |
| 'Accept': 'application/json', |
| }, |
| }); |
|
|
| if (!response.ok) { |
| throw new Error(`获取失败: ${response.status} ${response.statusText}`); |
| } |
|
|
| const text = await response.text(); |
| return parseSourcesFromJson(text); |
| } |
|
|
| |
| |
| |
| export function createSubscription(name: string, url: string): SourceSubscription { |
| return { |
| id: `sub_${Date.now()}_${Math.random().toString(36).substring(7)}`, |
| name: name.trim() || '未命名订阅', |
| url: url.trim(), |
| lastUpdated: Date.now(), |
| autoRefresh: true, |
| }; |
| } |
|
|
| |
| |
| |
| export function mergeSources( |
| existing: VideoSource[], |
| newSources: VideoSource[] |
| ): VideoSource[] { |
| const existingIds = new Set(existing.map(s => s.id)); |
| const merged = [...existing]; |
|
|
| for (const source of newSources) { |
| if (existingIds.has(source.id)) { |
| |
| const idx = merged.findIndex(s => s.id === source.id); |
| if (idx !== -1) { |
| merged[idx] = { ...merged[idx], ...source }; |
| } |
| } else { |
| |
| merged.push({ |
| ...source, |
| priority: merged.length + 1, |
| }); |
| existingIds.add(source.id); |
| } |
| } |
|
|
| return merged; |
| } |
|
|