网盘搜索,磁链类型增加存到私人影库功能
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
import {
|
import {
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
Copy,
|
Copy,
|
||||||
|
Download,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Loader2,
|
Loader2,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
@@ -21,6 +22,14 @@ interface PansouSearchProps {
|
|||||||
onError?: (error: string) => void;
|
onError?: (error: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DownloadTool = 'aria2' | 'Transmission' | 'qBittorrent';
|
||||||
|
|
||||||
|
const downloadToolOptions: Array<{ value: DownloadTool; label: string }> = [
|
||||||
|
{ value: 'aria2', label: 'aria2' },
|
||||||
|
{ value: 'qBittorrent', label: 'qBittorrent' },
|
||||||
|
{ value: 'Transmission', label: 'Transmission' },
|
||||||
|
];
|
||||||
|
|
||||||
// 网盘类型映射
|
// 网盘类型映射
|
||||||
const CLOUD_TYPE_NAMES: Record<string, string> = {
|
const CLOUD_TYPE_NAMES: Record<string, string> = {
|
||||||
baidu: '百度网盘',
|
baidu: '百度网盘',
|
||||||
@@ -154,6 +163,12 @@ export default function PansouSearch({
|
|||||||
const typeDragScrollLeftRef = useRef(0);
|
const typeDragScrollLeftRef = useRef(0);
|
||||||
const [transferingUrl, setTransferingUrl] = useState<string | null>(null);
|
const [transferingUrl, setTransferingUrl] = useState<string | null>(null);
|
||||||
const [playingUrl, setPlayingUrl] = useState<string | null>(null);
|
const [playingUrl, setPlayingUrl] = useState<string | null>(null);
|
||||||
|
const [downloadingUrl, setDownloadingUrl] = useState<string | null>(null);
|
||||||
|
const [showNameDialog, setShowNameDialog] = useState(false);
|
||||||
|
const [selectedDownloadLink, setSelectedDownloadLink] =
|
||||||
|
useState<PansouLink | null>(null);
|
||||||
|
const [customName, setCustomName] = useState('');
|
||||||
|
const [downloadTool, setDownloadTool] = useState<DownloadTool>('aria2');
|
||||||
const [toast, setToast] = useState<ToastProps | null>(null);
|
const [toast, setToast] = useState<ToastProps | null>(null);
|
||||||
const [cooldownRemainingMs, setCooldownRemainingMs] = useState(0);
|
const [cooldownRemainingMs, setCooldownRemainingMs] = useState(0);
|
||||||
const [checkStatesByType, setCheckStatesByType] = useState<
|
const [checkStatesByType, setCheckStatesByType] = useState<
|
||||||
@@ -335,6 +350,64 @@ export default function PansouSearch({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenDownloadDialog = (link: PansouLink) => {
|
||||||
|
setSelectedDownloadLink(link);
|
||||||
|
setCustomName(keyword.trim() || link.note || '');
|
||||||
|
setShowNameDialog(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseDownloadDialog = () => {
|
||||||
|
setShowNameDialog(false);
|
||||||
|
setSelectedDownloadLink(null);
|
||||||
|
setCustomName('');
|
||||||
|
setDownloadTool('aria2');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirmDownload = async () => {
|
||||||
|
if (!selectedDownloadLink || !customName.trim()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDownloadingUrl(selectedDownloadLink.url);
|
||||||
|
setShowNameDialog(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/acg/download', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
url: selectedDownloadLink.url,
|
||||||
|
name: customName.trim(),
|
||||||
|
tool: downloadTool,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || '添加下载任务失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
setToast({
|
||||||
|
message: data.message || '已添加到离线下载队列',
|
||||||
|
type: 'success',
|
||||||
|
onClose: () => setToast(null),
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
setToast({
|
||||||
|
message: err?.message || '添加下载任务失败',
|
||||||
|
type: 'error',
|
||||||
|
onClose: () => setToast(null),
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setDownloadingUrl(null);
|
||||||
|
setSelectedDownloadLink(null);
|
||||||
|
setCustomName('');
|
||||||
|
setDownloadTool('aria2');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleNetdiskInstantPlay = async (
|
const handleNetdiskInstantPlay = async (
|
||||||
cloudType: string,
|
cloudType: string,
|
||||||
link: PansouLink
|
link: PansouLink
|
||||||
@@ -798,6 +871,28 @@ export default function PansouSearch({
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{cloudType === 'magnet' && (
|
||||||
|
<button
|
||||||
|
onClick={() => handleOpenDownloadDialog(link)}
|
||||||
|
disabled={downloadingUrl === link.url}
|
||||||
|
className='flex items-center gap-1.5 px-2 py-1 rounded-md bg-green-600 hover:bg-green-700 text-white text-xs transition-colors disabled:opacity-60'
|
||||||
|
title='存到私人影库'
|
||||||
|
>
|
||||||
|
{downloadingUrl === link.url ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className='h-3.5 w-3.5 animate-spin' />
|
||||||
|
<span className='hidden sm:inline'>下载中...</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Download className='h-3.5 w-3.5' />
|
||||||
|
<span className='hidden sm:inline'>
|
||||||
|
存到私人影库
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleCopy(
|
handleCopy(
|
||||||
@@ -877,6 +972,52 @@ export default function PansouSearch({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='space-y-6'>{renderBody()}</div>
|
<div className='space-y-6'>{renderBody()}</div>
|
||||||
|
{showNameDialog && (
|
||||||
|
<div className='fixed inset-0 z-[1000] flex items-center justify-center bg-black/50'>
|
||||||
|
<div className='bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md w-full mx-4 shadow-xl'>
|
||||||
|
<h3 className='text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4'>
|
||||||
|
设置资源名称
|
||||||
|
</h3>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
value={customName}
|
||||||
|
onChange={(e) => setCustomName(e.target.value)}
|
||||||
|
placeholder='请输入资源名称'
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-green-500'
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<label className='mt-4 block text-sm font-medium text-gray-700 dark:text-gray-300'>
|
||||||
|
下载方式
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={downloadTool}
|
||||||
|
onChange={(e) => setDownloadTool(e.target.value as DownloadTool)}
|
||||||
|
className='mt-1 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-green-500'
|
||||||
|
>
|
||||||
|
{downloadToolOptions.map((option) => (
|
||||||
|
<option key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<div className='mt-4 flex gap-2 justify-end'>
|
||||||
|
<button
|
||||||
|
onClick={handleCloseDownloadDialog}
|
||||||
|
className='px-4 py-2 rounded-lg bg-gray-200 text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 transition-colors'
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleConfirmDownload}
|
||||||
|
disabled={!customName.trim()}
|
||||||
|
className='px-4 py-2 rounded-lg bg-green-600 text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors'
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{toast && <Toast {...toast} />}
|
{toast && <Toast {...toast} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user