Conversation
There was a problem hiding this comment.
Pull request overview
This PR enhances the frontend offline download workflow to support torrent files, magnet links, and ED2K links, aligning the UI with newly introduced backend torrent APIs (parse / rapid-upload) and adding a richer “BT Download” flow (file list display, save-path selection, tool filtering).
Changes:
- Added torrent-related frontend types and API helpers (parse/upload-parse/rapid-upload).
- Introduced an enhanced offline download modal with Link/BT tabs and a torrent file tree selector UI.
- Added a
.torrentcontext-menu entry to parse a torrent and open the enhanced modal, plus related i18n and toolbar wiring.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/bus.ts | Adds torrent_parsed event payload typing for opening the enhanced modal with parsed torrent data. |
| src/utils/api.ts | Adds torrent parse/upload-parse/rapid-upload API wrappers. |
| src/types/torrent.ts | Introduces TS interfaces for torrent parse/rapid-upload responses. |
| src/types/index.ts | Re-exports torrent types. |
| src/pages/home/toolbar/TorrentFileList.tsx | New torrent file tree component with checkbox selection support. |
| src/pages/home/toolbar/OfflineDownloadEnhanced.tsx | New enhanced offline download modal (tabs, torrent parsing, CAS rapid upload, tool filtering). |
| src/pages/home/toolbar/Toolbar.tsx | Switches modal registration from old OfflineDownload to OfflineDownloadEnhanced. |
| src/pages/home/toolbar/operations.ts | Adds offline_download_torrent operation/icon for context menu display. |
| src/pages/home/folder/context-menu.tsx | Adds .torrent right-click “offline download” entry that downloads + parses torrent and opens modal. |
| src/lang/en/home.json | Adds i18n strings for the enhanced offline download UI and new menu entry. |
| .codebuddy/plan/offline-download-enhancement/task-item.md | Planning doc for implementation steps. |
| .codebuddy/plan/offline-download-enhancement/requirements.md | Requirements doc describing expected behavior and acceptance criteria. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return r.post("/fs/torrent/upload_parse", formData, { | ||
| headers: { "Content-Type": "multipart/form-data" }, | ||
| }) |
| if (e.dataTransfer?.files.length) { | ||
| for (const file of e.dataTransfer.files) { | ||
| if (file.name.toLowerCase().endsWith(".torrent")) { | ||
| handleTorrentFile(file) | ||
| return | ||
| } | ||
| } | ||
| // 如果在链接 Tab 中拖入 torrent,转换为磁力链 | ||
| if (activeTab() === "link") { | ||
| const values: string[] = [] |
| // 当有 CAS 信息时,默认使用天翼云秒传(不需要 aria2) | ||
| const shouldUseCasRapidUpload = createMemo(() => { | ||
| return activeTab() === "bt" && !!torrentInfo()?.has_cas |
| // 秒传失败,提示用户 | ||
| notificationService.show({ | ||
| status: "warning", | ||
| title: t( | ||
| "home.toolbar.offline_download_enhanced.cas_rapid_upload_failed", | ||
| ), | ||
| description: resp.message, | ||
| }) | ||
| } | ||
| } catch (err) { | ||
| // 秒传异常,提示用户 | ||
| notificationService.show({ | ||
| status: "danger", | ||
| title: t( | ||
| "home.toolbar.offline_download_enhanced.cas_rapid_upload_failed", | ||
| ), | ||
| description: String(err), | ||
| }) | ||
| } finally { | ||
| setRapidUploading(false) | ||
| } | ||
| // CAS 秒传失败后不自动回退,直接返回让用户决定 | ||
| return | ||
| } |
| // 正常离线下载:将 torrent 转为磁力链提交 | ||
| const buffer = Uint8Array.from(atob(torrentData()), (c) => | ||
| c.charCodeAt(0), | ||
| ) | ||
| const magnetUrl = toMagnetUrl(buffer) | ||
| const resp = await offlineDownload( | ||
| savePath(), | ||
| [magnetUrl], | ||
| tool(), | ||
| deletePolicy(), | ||
| ) | ||
| handleRespWithNotifySuccess(resp, () => { |
| if (data.announce) { | ||
| params.tr = utf8Decode(data.announce) | ||
| } | ||
| return `magnet:?xt=urn:btih:${infoHash}&${new URLSearchParams(params).toString()}` |
| hidden={() => { | ||
| return ( | ||
| isShare() || | ||
| !oneChecked() || | ||
| selectedObjs().some((o) => o.is_dir) || | ||
| !selectedObjs().every((o) => | ||
| o.name.toLowerCase().endsWith(".torrent"), | ||
| ) | ||
| ) |
| const isChecked = createMemo(() => { | ||
| if (!props.node.isDir) { | ||
| return props.selectedFiles.includes(props.node.fileIndex!) | ||
| } | ||
| const indices = getAllFileIndices(props.node) | ||
| return indices.every((i) => props.selectedFiles.includes(i)) | ||
| }) | ||
|
|
||
| const isIndeterminate = createMemo(() => { | ||
| if (!props.node.isDir) return false | ||
| const indices = getAllFileIndices(props.node) | ||
| const checkedCount = indices.filter((i) => | ||
| props.selectedFiles.includes(i), | ||
| ).length | ||
| return checkedCount > 0 && checkedCount < indices.length | ||
| }) |
|
@copilot apply changes based on the comments in this thread |
- Fix torrentUploadParse: remove explicit Content-Type header to allow Axios to set boundary automatically
- Fix toMagnetUrl: remove trailing '&' when no extra params
- Fix handleDrop: branch by activeTab() - BT tab parses torrent, Link tab converts to magnet
- Fix shouldUseCasRapidUpload: add casRapidUploadFailed state to enable fallback after CAS failure
- Fix CAS fallback: after rapid upload failure, set casRapidUploadFailed flag so user can proceed with normal offline download
- Fix file selection: add informational note that partial selection is not yet supported by backend
- Fix context menu: add userCan('offline_download') && objStore.write permission checks for .torrent menu item
- Optimize TorrentFileList: use Set for O(1) selection lookups and cache directory descendant indices
- Add new i18n keys: cas_failed_fallback_hint, file_selection_hint
Agent-Logs-Url: https://github.com/OpenListTeam/OpenList-Frontend/sessions/075a5707-3520-4dea-ada1-b54e89521fb4
Applied all changes from the review thread in commit 4625d7e:
The |
Description / 描述
支持BT种子文件/ED2K/磁力链离线下载,支持天翼云CAS秒传
功能支持项
ed2k://和magnet:?链接发起离线下载任务预期的逻辑
后端改动
新增文件
pkg/torrent/bencode.gopkg/torrent/torrent.gopkg/torrent/hash_writer.gopkg/torrent/generate.godrivers/189pc/torrent.godrivers/189/torrent.goserver/handles/torrent.go修改文件
drivers/189pc/meta.goGenerateTorrent配置选项drivers/189pc/utils.godrivers/189/util.gointernal/offline_download/tool/transfer.goserver/router.go新增 API
/api/fs/torrent/parse/api/fs/torrent/upload_parse/api/fs/torrent/rapid_upload/api/fs/torrent/generate前端改动
新增文件
src/types/torrent.tssrc/pages/home/toolbar/OfflineDownloadEnhanced.tsxsrc/pages/home/toolbar/TorrentFileList.tsx修改文件
src/types/index.tssrc/utils/api.tssrc/utils/bus.tstorrent_parsed事件类型src/pages/home/toolbar/operations.tsoffline_download_torrent操作图标src/pages/home/toolbar/Toolbar.tsxsrc/pages/home/folder/context-menu.tsxsrc/lang/en/home.jsonBT文件逻辑
graph TD A[上传文件] --> B[按 10MB 分片] B --> C[每片计算 MD5<br/>用于天翼云秒传] B --> D[每片计算 SHA-1<br/>用于 BT piece hash] C --> E[fileMD5 + sliceMD5] D --> F[pieces 字段] E --> G[x-cas 扩展<br/>存储在 info 外部] F --> H[info 字典] G --> I[生成 .torrent 文件] H --> I J[收到 .torrent] --> K{有 x-cas?} K -->|是| L[提取 fileMD5 + sliceMD5] L --> M[调用秒传接口] M --> N[文件恢复成功] K -->|否| O[需要下载后计算 MD5] O --> P[注入 CAS 信息到 torrent]离线下载完整流程
graph TD A[用户上传/选择 .torrent] --> B[解析 torrent] B --> C{有 CAS 信息?} C -->|是| D[直接 CAS 秒传<br/>不需要选择下载工具] D --> E{秒传成功?} E -->|是| F[完成 ✅] E -->|否| G[提示失败<br/>用户决定下一步] C -->|否| H[选择下载工具] H --> I[提交离线下载] I --> J[aria2/qBit 下载] J --> K[下载完成] K --> L{目标是天翼云?} L -->|是| M[计算 MD5 尝试秒传] M --> N{秒传成功?} N -->|是| F N -->|否| O[普通上传] O --> F L -->|否| OCAS 秒传接口说明
秒传使用新版
initMultiUpload接口(同时传fileMd5+sliceMd5+sliceSize),与正常上传使用的接口完全一致,确保能匹配到通过新版接口上传的文件。旧版createUploadFile.action接口作为回退方案。设计要点
pkg/torrent包不依赖任何驱动,所有存储均可使用initMultiUpload+commitMultiUploadFile接口,兼容新旧上传方式Motivation and Context / 背景
实现 BT 种子生态与网盘秒传的融合,使得:
How Has This Been Tested? / 测试
Checklist / 检查清单
go fmtor prettier.我已使用
go fmt或 prettier 格式化提交的代码。我已为此 PR 添加了适当的标签(如无权限或需要的标签不存在,请在描述中说明,管理员将后续处理)。
我已在适当情况下使用"Request review"功能请求相关代码作者进行审查。
我已阅读 CONTRIBUTING 文档。
我已相应更新了相关仓库(若适用)。