From 50e5a4182a62ac39b5588ddbadd9c2b145b475dd Mon Sep 17 00:00:00 2001 From: William Date: Mon, 29 Jun 2026 18:21:06 +0600 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=89=B9=E9=87=8F=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=B5=8F=E8=A7=88=E5=99=A8=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E4=B8=8E=E4=B8=8B=E8=BD=BD=E5=B7=A5=E5=85=B7=E4=B8=A4=E7=A7=8D?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 弹窗使用 Tabs 切换两种下载方式 - 浏览器下载保留逐条触发逻辑 - 下载工具页提供 aria2 / wget 命令示例与复制全部链接 --- apps/web/src/pages/Videos.tsx | 142 ++++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 32 deletions(-) diff --git a/apps/web/src/pages/Videos.tsx b/apps/web/src/pages/Videos.tsx index 31f3918..8c52a24 100644 --- a/apps/web/src/pages/Videos.tsx +++ b/apps/web/src/pages/Videos.tsx @@ -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([]); 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() { /> setDownloadModalOpen(false)} footer={[ - , , ]} > -
- {downloadResults.map((item) => ( -
- {item.title || item.videoId} -
    - {item.urls.map((url, idx) => ( -
  • - {url.url} - {url.definition} -
  • - ))} -
-
- ))} -
+ + + + 将逐个创建下载任务,间隔 300ms,降低被浏览器拦截的概率。若被拦截,请使用「下载工具」方式。 + +
+ {downloadResults.map((item) => ( +
+ {item.title || item.videoId} +
    + {item.urls.map((url, idx) => ( +
  • + {url.url} + {url.definition} +
  • + ))} +
+
+ ))} +
+ + ), + }, + { + key: 'tools', + label: '下载工具', + children: ( + + +
+ 推荐方式 +
    +
  • + + 复制全部链接后,使用 aria2 或{' '} + wget 等工具批量下载。 + +
  • +
  • + aria2 支持断点续传、多线程,适合下载大文件。 +
  • +
+ aria2 示例 +
+                      {aria2Command || '# 暂无下载链接'}
+                    
+ wget 示例 +
+                      {wgetCommand || '# 暂无下载链接'}
+                    
+
+
+ ), + }, + ]} + />