import { FormEvent, useState } from 'react' import toast from 'react-hot-toast' import { Download, Search } from 'lucide-react' import { sitesAPI, type SiteSearchResult } from '../api/sites' import { downloadsAPI } from '../api/downloads' function fmtBytes(n: number): string { if (!n || n <= 0) return '—' const u = ['B', 'KB', 'MB', 'GB', 'TB'] let v = n let i = 0 while (v >= 1024 && i < u.length - 1) { v /= 1024 i++ } return `${v.toFixed(1)} ${u[i]}` } // SiteSearchPage performs cross-site torrent search across all enabled // sites and displays a merged result table sorted by seeders. export function SiteSearchPage() { const [keyword, setKeyword] = useState('') const [results, setResults] = useState([]) const [loading, setLoading] = useState(false) const [searched, setSearched] = useState(false) const onSubmit = async (e: FormEvent) => { e.preventDefault() if (!keyword.trim()) return setLoading(true) setSearched(true) try { const data = await sitesAPI.search(keyword.trim()) setResults(data.items || []) if ((data.items || []).length === 0) { toast('未找到结果,请检查站点配置或更换关键词') } } catch (err: unknown) { const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? '搜索失败' toast.error(msg) } finally { setLoading(false) } } const downloadTorrent = async (item: SiteSearchResult) => { const url = item.download_url || item.torrent_url if (!url) { toast.error('无可用下载链接') return } try { await downloadsAPI.add(url, '', { title: item.title, source_category: item.category, }) toast.success(`已加入下载: ${item.title.substring(0, 40)}...`) } catch (err: unknown) { const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? '下载失败' toast.error(msg) } } return (

站点搜索

跨站搜索所有已启用的 PT/BT 站点,结果按做种数排序。

setKeyword(e.target.value)} />
{loading &&

正在查询所有站点…

} {!loading && searched && results.length === 0 && (

未找到结果。请确认已在「站点管理」中添加并启用了至少一个站点,且 Cookie / API Key 有效。

)} {results.length > 0 && (
{results.map((item, idx) => ( ))}
站点 标题 大小 S L Free 操作
{item.site_name} {item.title.length > 80 ? item.title.substring(0, 80) + '…' : item.title} {fmtBytes(item.size)} {item.seeders || '—'} {item.leechers || '—'} {item.free && ( Free )}
)}
) }