- 通过创建隐藏 a 标签并点击触发浏览器下载 - 多个下载任务间隔 300ms 触发,降低被浏览器拦截的概率 - 保留复制链接功能作为备选
This commit is contained in:
@@ -41,6 +41,16 @@ const formatSize = (bytes?: number) => {
|
||||
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() {
|
||||
const user = useAuthStore((state) => state.user);
|
||||
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 ids = selectedRows.map((v) => v.videoId);
|
||||
try {
|
||||
const results = await batchDownload(ids);
|
||||
const valid = results.filter((r) => r.urls.length > 0);
|
||||
showDownloadResults(valid);
|
||||
setDownloadResults(valid);
|
||||
setDownloadModalOpen(true);
|
||||
setSelectedRows([]);
|
||||
} catch (error) {
|
||||
message.error('获取下载地址失败');
|
||||
}
|
||||
};
|
||||
|
||||
const showDownloadResults = (results: DownloadResult[]) => {
|
||||
Modal.info({
|
||||
title: '批量下载地址',
|
||||
width: 800,
|
||||
content: (
|
||||
<div style={{ maxHeight: 400, overflow: 'auto' }}>
|
||||
{results.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>
|
||||
),
|
||||
const startDownloads = () => {
|
||||
let count = 0;
|
||||
downloadResults.forEach((item) => {
|
||||
item.urls.forEach((urlInfo) => {
|
||||
const link = document.createElement('a');
|
||||
link.href = urlInfo.url;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noreferrer';
|
||||
link.download = `${item.title || item.videoId}_${urlInfo.definition}.${getExtFromUrl(urlInfo.url)}`;
|
||||
document.body.appendChild(link);
|
||||
setTimeout(() => {
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}, count * 300);
|
||||
count++;
|
||||
});
|
||||
});
|
||||
message.success(`已尝试触发 ${count} 个下载任务`);
|
||||
};
|
||||
|
||||
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
|
||||
title="视频详情"
|
||||
width={560}
|
||||
|
||||
Reference in New Issue
Block a user