feat: 批量下载弹窗增加开始下载按钮
CI / lint-and-build (push) Has been cancelled

- 通过创建隐藏 a 标签并点击触发浏览器下载

- 多个下载任务间隔 300ms 触发,降低被浏览器拦截的概率

- 保留复制链接功能作为备选
This commit is contained in:
2026-06-29 18:18:04 +06:00
parent 962cbc8df3
commit a90fd3cf84
+63 -22
View File
@@ -41,6 +41,16 @@ const formatSize = (bytes?: number) => {
return `${size.toFixed(2)} ${units[i]}`; return `${size.toFixed(2)} ${units[i]}`;
}; };
const getExtFromUrl = (url: string): string => {
try {
const pathname = new URL(url).pathname;
const ext = pathname.split('.').pop();
return ext && ext.length <= 5 ? ext : 'mp4';
} catch {
return 'mp4';
}
};
export default function VideosPage() { export default function VideosPage() {
const user = useAuthStore((state) => state.user); const user = useAuthStore((state) => state.user);
const [videos, setVideos] = useState<VideoItem[]>([]); const [videos, setVideos] = useState<VideoItem[]>([]);
@@ -115,40 +125,40 @@ export default function VideosPage() {
} }
}; };
const [downloadResults, setDownloadResults] = useState<DownloadResult[]>([]);
const [downloadModalOpen, setDownloadModalOpen] = useState(false);
const handleBatchDownload = async () => { const handleBatchDownload = async () => {
const ids = selectedRows.map((v) => v.videoId); const ids = selectedRows.map((v) => v.videoId);
try { try {
const results = await batchDownload(ids); const results = await batchDownload(ids);
const valid = results.filter((r) => r.urls.length > 0); const valid = results.filter((r) => r.urls.length > 0);
showDownloadResults(valid); setDownloadResults(valid);
setDownloadModalOpen(true);
setSelectedRows([]); setSelectedRows([]);
} catch (error) { } catch (error) {
message.error('获取下载地址失败'); message.error('获取下载地址失败');
} }
}; };
const showDownloadResults = (results: DownloadResult[]) => { const startDownloads = () => {
Modal.info({ let count = 0;
title: '批量下载地址', downloadResults.forEach((item) => {
width: 800, item.urls.forEach((urlInfo) => {
content: ( const link = document.createElement('a');
<div style={{ maxHeight: 400, overflow: 'auto' }}> link.href = urlInfo.url;
{results.map((item) => ( link.target = '_blank';
<div key={item.videoId} style={{ marginBottom: 12 }}> link.rel = 'noreferrer';
<Text strong>{item.title || item.videoId}</Text> link.download = `${item.title || item.videoId}_${urlInfo.definition}.${getExtFromUrl(urlInfo.url)}`;
<ul style={{ margin: 4, paddingLeft: 20 }}> document.body.appendChild(link);
{item.urls.map((url, idx) => ( setTimeout(() => {
<li key={idx}> link.click();
<Text copyable>{url.url}</Text> document.body.removeChild(link);
<Tag style={{ marginLeft: 8 }}>{url.definition}</Tag> }, count * 300);
</li> count++;
))}
</ul>
</div>
))}
</div>
),
}); });
});
message.success(`已尝试触发 ${count} 个下载任务`);
}; };
const handleRowClick = (record: VideoItem) => { const handleRowClick = (record: VideoItem) => {
@@ -284,6 +294,37 @@ export default function VideosPage() {
}} }}
/> />
<Modal
title="批量下载地址"
open={downloadModalOpen}
width={800}
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>
</Modal>
<Drawer <Drawer
title="视频详情" title="视频详情"
width={560} width={560}