feat: 批量下载支持浏览器下载与下载工具两种方式
CI / lint-and-build (push) Has been cancelled

- 弹窗使用 Tabs 切换两种下载方式

- 浏览器下载保留逐条触发逻辑

- 下载工具页提供 aria2 / wget 命令示例与复制全部链接
This commit is contained in:
2026-06-29 18:21:06 +06:00
parent a90fd3cf84
commit 50e5a4182a
+96 -18
View File
@@ -13,6 +13,7 @@ import {
Image, Image,
Popconfirm, Popconfirm,
Typography, Typography,
Tabs,
} from 'antd'; } from 'antd';
import { DownloadOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons'; import { DownloadOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@@ -128,20 +129,20 @@ export default function VideosPage() {
const [downloadResults, setDownloadResults] = useState<DownloadResult[]>([]); const [downloadResults, setDownloadResults] = useState<DownloadResult[]>([]);
const [downloadModalOpen, setDownloadModalOpen] = useState(false); const [downloadModalOpen, setDownloadModalOpen] = useState(false);
const handleBatchDownload = async () => { const allDownloadUrls = downloadResults.flatMap((item) =>
const ids = selectedRows.map((v) => v.videoId); item.urls.map((u) => u.url)
try { );
const results = await batchDownload(ids);
const valid = results.filter((r) => r.urls.length > 0); const wgetCommand = allDownloadUrls.map((url) => `wget "${url}"`).join('\n');
setDownloadResults(valid); const aria2Command = allDownloadUrls.map((url) => `aria2c "${url}"`).join('\n');
setDownloadModalOpen(true);
setSelectedRows([]); const copyAllUrls = () => {
} catch (error) { navigator.clipboard.writeText(allDownloadUrls.join('\n')).then(() => {
message.error('获取下载地址失败'); message.success('已复制全部链接');
} });
}; };
const startDownloads = () => { const startBrowserDownloads = () => {
let count = 0; let count = 0;
downloadResults.forEach((item) => { downloadResults.forEach((item) => {
item.urls.forEach((urlInfo) => { item.urls.forEach((urlInfo) => {
@@ -161,6 +162,19 @@ export default function VideosPage() {
message.success(`已尝试触发 ${count} 个下载任务`); message.success(`已尝试触发 ${count} 个下载任务`);
}; };
const handleBatchDownload = async () => {
const ids = selectedRows.map((v) => v.videoId);
try {
const results = await batchDownload(ids);
const valid = results.filter((r) => r.urls.length > 0);
setDownloadResults(valid);
setDownloadModalOpen(true);
setSelectedRows([]);
} catch (error) {
message.error('获取下载地址失败');
}
};
const handleRowClick = (record: VideoItem) => { const handleRowClick = (record: VideoItem) => {
setDetail(record); setDetail(record);
setDrawerOpen(true); setDrawerOpen(true);
@@ -295,20 +309,30 @@ export default function VideosPage() {
/> />
<Modal <Modal
title="批量下载地址" title="批量下载"
open={downloadModalOpen} open={downloadModalOpen}
width={800} width={820}
onCancel={() => setDownloadModalOpen(false)} onCancel={() => setDownloadModalOpen(false)}
footer={[ footer={[
<Button key="download" type="primary" onClick={startDownloads}>
</Button>,
<Button key="close" onClick={() => setDownloadModalOpen(false)}> <Button key="close" onClick={() => setDownloadModalOpen(false)}>
</Button>, </Button>,
]} ]}
> >
<div style={{ maxHeight: 400, overflow: 'auto' }}> <Tabs
items={[
{
key: 'browser',
label: '浏览器下载',
children: (
<Space direction="vertical" style={{ width: '100%' }}>
<Button type="primary" onClick={startBrowserDownloads}>
</Button>
<Text type="secondary">
300ms使
</Text>
<div style={{ maxHeight: 320, overflow: 'auto' }}>
{downloadResults.map((item) => ( {downloadResults.map((item) => (
<div key={item.videoId} style={{ marginBottom: 12 }}> <div key={item.videoId} style={{ marginBottom: 12 }}>
<Text strong>{item.title || item.videoId}</Text> <Text strong>{item.title || item.videoId}</Text>
@@ -323,6 +347,60 @@ export default function VideosPage() {
</div> </div>
))} ))}
</div> </div>
</Space>
),
},
{
key: 'tools',
label: '下载工具',
children: (
<Space direction="vertical" style={{ width: '100%' }}>
<Button onClick={copyAllUrls}></Button>
<div style={{ background: '#f6f8fa', padding: 16, borderRadius: 8 }}>
<Text strong></Text>
<ul>
<li>
<Text>
使 <Text code>aria2</Text> {' '}
<Text code>wget</Text>
</Text>
</li>
<li>
<Text>aria2 线</Text>
</li>
</ul>
<Text strong>aria2 </Text>
<pre
style={{
background: '#1e1e1e',
color: '#d4d4d4',
padding: 12,
borderRadius: 4,
overflow: 'auto',
fontSize: 12,
}}
>
{aria2Command || '# 暂无下载链接'}
</pre>
<Text strong>wget </Text>
<pre
style={{
background: '#1e1e1e',
color: '#d4d4d4',
padding: 12,
borderRadius: 4,
overflow: 'auto',
fontSize: 12,
}}
>
{wgetCommand || '# 暂无下载链接'}
</pre>
</div>
</Space>
),
},
]}
/>
</Modal> </Modal>
<Drawer <Drawer