diff --git a/app/api/detail/route.ts b/app/api/detail/route.ts index 3c9c3fd..0d36846 100644 --- a/app/api/detail/route.ts +++ b/app/api/detail/route.ts @@ -27,11 +27,19 @@ async function handleDetailRequest(id: string | null, source: string | null, met ); } - const sourceConfig = getSourceById(source); + let sourceConfig; + + // If source is an object (from POST), use it + if (typeof source === 'object') { + sourceConfig = source; + } else { + // If source is a string ID (from GET), try to look it up + sourceConfig = getSourceById(source); + } if (!sourceConfig) { return NextResponse.json( - { error: 'Invalid source ID' }, + { error: 'Invalid source configuration' }, { status: 400 } ); } diff --git a/app/api/search-parallel/route.ts b/app/api/search-parallel/route.ts index e425eb6..0620dac 100644 --- a/app/api/search-parallel/route.ts +++ b/app/api/search-parallel/route.ts @@ -16,7 +16,7 @@ export async function POST(request: NextRequest) { async start(controller) { try { const body = await request.json(); - const { query, sources: sourceIds, page = 1 } = body; + const { query, sources: sourceConfigs, page = 1 } = body; // Validate input if (!query || typeof query !== 'string' || query.trim().length === 0) { @@ -28,15 +28,15 @@ export async function POST(request: NextRequest) { return; } - // Get source configurations - const sources = sourceIds - .map((id: string) => getSourceById(id)) - .filter((source: any): source is NonNullable => source !== undefined); + // Use provided sources or fallback to empty (client should provide them) + const sources = Array.isArray(sourceConfigs) && sourceConfigs.length > 0 + ? sourceConfigs + : []; if (sources.length === 0) { controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'error', - message: 'No valid sources' + message: 'No valid sources provided' })}\n\n`)); controller.close(); return; diff --git a/app/page.tsx b/app/page.tsx index ee68730..eba8c0f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -67,7 +67,10 @@ function HomePage() { const handleSearch = (searchQuery: string) => { setQuery(searchQuery); setHasSearched(true); - performSearch(searchQuery, currentSortBy as any); + const settings = settingsStore.getSettings(); + // Filter enabled sources + const enabledSources = settings.sources.filter(s => s.enabled); + performSearch(searchQuery, enabledSources, currentSortBy as any); }; const handleReset = () => { diff --git a/app/settings/hooks/useSettingsPage.ts b/app/settings/hooks/useSettingsPage.ts index 39926a1..b94e0f1 100644 --- a/app/settings/hooks/useSettingsPage.ts +++ b/app/settings/hooks/useSettingsPage.ts @@ -10,6 +10,7 @@ export function useSettingsPage() { const [isImportModalOpen, setIsImportModalOpen] = useState(false); const [isResetDialogOpen, setIsResetDialogOpen] = useState(false); const [isRestoreDefaultsDialogOpen, setIsRestoreDefaultsDialogOpen] = useState(false); + const [editingSource, setEditingSource] = useState(null); useEffect(() => { const settings = settingsStore.getSettings(); @@ -23,8 +24,17 @@ export function useSettingsPage() { }; const handleAddSource = (source: VideoSource) => { - const updated = [...sources, source]; + const exists = sources.some(s => s.id === source.id); + const updated = exists + ? sources.map(s => s.id === source.id ? source : s) + : [...sources, source]; handleSourcesChange(updated); + setEditingSource(null); + }; + + const handleEditSource = (source: VideoSource) => { + setEditingSource(source); + setIsAddModalOpen(true); }; const handleSortChange = (newSort: SortOption) => { @@ -78,6 +88,7 @@ export function useSettingsPage() { setIsImportModalOpen, setIsResetDialogOpen, setIsRestoreDefaultsDialogOpen, + setEditingSource, handleSourcesChange, handleAddSource, handleSortChange, @@ -85,5 +96,7 @@ export function useSettingsPage() { handleImport, handleRestoreDefaults, handleResetAll, + editingSource, + handleEditSource, }; } diff --git a/app/settings/page.tsx b/app/settings/page.tsx index d1f8ab7..18fc909 100644 --- a/app/settings/page.tsx +++ b/app/settings/page.tsx @@ -31,6 +31,9 @@ export default function SettingsPage() { handleImport, handleRestoreDefaults, handleResetAll, + editingSource, + handleEditSource, + setEditingSource, } = useSettingsPage(); return ( @@ -44,7 +47,11 @@ export default function SettingsPage() { sources={sources} onSourcesChange={handleSourcesChange} onRestoreDefaults={() => setIsRestoreDefaultsDialogOpen(true)} - onAddSource={() => setIsAddModalOpen(true)} + onAddSource={() => { + setEditingSource(null); + setIsAddModalOpen(true); + }} + onEditSource={handleEditSource} /> {/* Sort Options */} @@ -64,9 +71,13 @@ export default function SettingsPage() { {/* Modals */} setIsAddModalOpen(false)} + onClose={() => { + setIsAddModalOpen(false); + setEditingSource(null); + }} onAdd={handleAddSource} existingIds={sources.map(s => s.id)} + initialValues={editingSource} /> void; onAdd: (source: VideoSource) => void; existingIds: string[]; + initialValues?: VideoSource | null; } -export function AddSourceModal({ isOpen, onClose, onAdd, existingIds }: AddSourceModalProps) { +export function AddSourceModal({ isOpen, onClose, onAdd, existingIds, initialValues }: AddSourceModalProps) { const { name, setName, url, setUrl, error, handleSubmit } = useAddSourceForm({ isOpen, existingIds, onAdd, onClose, + initialValues, }); if (!isOpen) return null; @@ -34,7 +36,7 @@ export function AddSourceModal({ isOpen, onClose, onAdd, existingIds }: AddSourc }`} >
- +
@@ -83,7 +85,7 @@ export function AddSourceModal({ isOpen, onClose, onAdd, existingIds }: AddSourc type="submit" className="flex-1 px-6 py-3 rounded-[var(--radius-2xl)] bg-[var(--accent-color)] text-white font-semibold hover:brightness-110 hover:-translate-y-0.5 shadow-[var(--shadow-sm)] transition-all duration-200" > - 添加 + {initialValues ? "保存" : "添加"}
diff --git a/components/settings/SourceManager.tsx b/components/settings/SourceManager.tsx index d3bdeba..da47ab9 100644 --- a/components/settings/SourceManager.tsx +++ b/components/settings/SourceManager.tsx @@ -5,37 +5,33 @@ import type { VideoSource } from '@/lib/types'; interface SourceManagerProps { sources: VideoSource[]; - onSourcesChange: (sources: VideoSource[]) => void; + onToggle: (id: string) => void; + onDelete: (id: string) => void; + onReorder: (id: string, direction: 'up' | 'down') => void; + onEdit?: (source: VideoSource) => void; + defaultIds: string[]; } -export function SourceManager({ sources, onSourcesChange }: SourceManagerProps) { +export function SourceManager({ + sources, + onToggle, + onDelete, + onReorder, + onEdit, + defaultIds +}: SourceManagerProps) { const [editingId, setEditingId] = useState(null); const handleToggle = (id: string) => { - const updated = sources.map(s => - s.id === id ? { ...s, enabled: !s.enabled } : s - ); - onSourcesChange(updated); + onToggle(id); }; const handleDelete = (id: string) => { - const updated = sources.filter(s => s.id !== id); - onSourcesChange(updated); + onDelete(id); }; const handlePriorityChange = (id: string, direction: 'up' | 'down') => { - const currentIndex = sources.findIndex(s => s.id === id); - if (currentIndex === -1) return; - - const newIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1; - if (newIndex < 0 || newIndex >= sources.length) return; - - const updated = [...sources]; - [updated[currentIndex], updated[newIndex]] = [updated[newIndex], updated[currentIndex]]; - - // Update priorities - updated.forEach((s, idx) => s.priority = idx + 1); - onSourcesChange(updated); + onReorder(id, direction); }; return ( @@ -55,8 +51,8 @@ export function SourceManager({ sources, onSourcesChange }: SourceManagerProps) > + {/* Edit Button - Only for custom sources */} + {onEdit && !defaultIds.includes(source.id) && ( + + )} + {/* Delete Button */}