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
+110 -32
View File
@@ -13,6 +13,7 @@ import {
Image,
Popconfirm,
Typography,
Tabs,
} from 'antd';
import { DownloadOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
@@ -128,20 +129,20 @@ export default function VideosPage() {
const [downloadResults, setDownloadResults] = useState<DownloadResult[]>([]);
const [downloadModalOpen, setDownloadModalOpen] = useState(false);
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 allDownloadUrls = downloadResults.flatMap((item) =>
item.urls.map((u) => u.url)
);
const wgetCommand = allDownloadUrls.map((url) => `wget "${url}"`).join('\n');
const aria2Command = allDownloadUrls.map((url) => `aria2c "${url}"`).join('\n');
const copyAllUrls = () => {
navigator.clipboard.writeText(allDownloadUrls.join('\n')).then(() => {
message.success('已复制全部链接');
});
};
const startDownloads = () => {
const startBrowserDownloads = () => {
let count = 0;
downloadResults.forEach((item) => {
item.urls.forEach((urlInfo) => {
@@ -161,6 +162,19 @@ export default function VideosPage() {
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) => {
setDetail(record);
setDrawerOpen(true);
@@ -295,34 +309,98 @@ export default function VideosPage() {
/>
<Modal
title="批量下载地址"
title="批量下载"
open={downloadModalOpen}
width={800}
width={820}
onCancel={() => setDownloadModalOpen(false)}
footer={[
<Button key="download" type="primary" onClick={startDownloads}>
</Button>,
<Button key="close" onClick={() => setDownloadModalOpen(false)}>
</Button>,
]}
>
<div style={{ maxHeight: 400, overflow: 'auto' }}>
{downloadResults.map((item) => (
<div key={item.videoId} style={{ marginBottom: 12 }}>
<Text strong>{item.title || item.videoId}</Text>
<ul style={{ margin: 4, paddingLeft: 20 }}>
{item.urls.map((url, idx) => (
<li key={idx}>
<Text copyable>{url.url}</Text>
<Tag style={{ marginLeft: 8 }}>{url.definition}</Tag>
</li>
))}
</ul>
</div>
))}
</div>
<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) => (
<div key={item.videoId} style={{ marginBottom: 12 }}>
<Text strong>{item.title || item.videoId}</Text>
<ul style={{ margin: 4, paddingLeft: 20 }}>
{item.urls.map((url, idx) => (
<li key={idx}>
<Text copyable>{url.url}</Text>
<Tag style={{ marginLeft: 8 }}>{url.definition}</Tag>
</li>
))}
</ul>
</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>
<Drawer