- 通过创建隐藏 a 标签并点击触发浏览器下载 - 多个下载任务间隔 300ms 触发,降低被浏览器拦截的概率 - 保留复制链接功能作为备选
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user