feat: 新增客户端协议生成系统,支持8种主流下载工具

🚀 核心功能
- 新增完整的客户端下载链接生成器系统
- 支持ARIA2、Motrix、比特彗星、迅雷、wget、cURL、IDM、FDM、PowerShell等8种客户端
- 自动处理防盗链参数(User-Agent、Referer、Cookie等)
- 提供可扩展的生成器架构,支持自定义客户端

🔧 技术实现
- ClientLinkGeneratorFactory: 工厂模式管理生成器
- DownloadLinkMeta: 元数据存储下载信息
- ClientLinkUtils: 便捷工具类
- 线程安全的ConcurrentHashMap设计

🌐 前端集成
- 新增ClientLinks.vue界面,支持客户端链接展示
- Element Plus图标系统,混合图标显示
- 客户端检测逻辑优化,避免自动打开外部应用
- 移动端和PC端环境判断

📚 文档完善
- 完整的CLIENT_LINK_GENERATOR_GUIDE.md使用指南
- API文档和测试用例
- 输出示例和最佳实践

从单纯的网盘解析工具升级为完整的下载解决方案生态
This commit is contained in:
q
2025-10-24 09:25:57 +08:00
parent 231d5c3fb9
commit 42b721eabf
47 changed files with 3740 additions and 96 deletions

View File

@@ -0,0 +1,120 @@
# 客户端下载链接 API 文档
## 概述
新增的客户端下载链接 API 允许用户获取各种下载客户端格式的下载链接,包括 cURL、PowerShell、Aria2、迅雷等。
## API 端点
### 1. 获取所有客户端下载链接
**端点**: `GET /v2/clientLinks`
**参数**:
- `url` (必需): 分享链接
- `pwd` (可选): 提取码
**响应示例**:
```json
{
"success": true,
"directLink": "https://example.com/file.zip",
"fileName": "test-file.zip",
"fileSize": 1024000,
"clientLinks": {
"CURL": "curl -L -H \"User-Agent: Mozilla/5.0...\" -o \"test-file.zip\" \"https://example.com/file.zip\"",
"POWERSHELL": "$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession...",
"ARIA2": "aria2c --header=\"User-Agent: Mozilla/5.0...\" --out=\"test-file.zip\" \"https://example.com/file.zip\"",
"THUNDER": "thunder://QUFodHRwczovL2V4YW1wbGUuY29tL2ZpbGUuemlwWlo=",
"IDM": "idm://https://example.com/file.zip",
"WGET": "wget --header=\"User-Agent: Mozilla/5.0...\" -O \"test-file.zip\" \"https://example.com/file.zip\"",
"BITCOMET": "bitcomet://https://example.com/file.zip",
"MOTRIX": "{\"url\":\"https://example.com/file.zip\",\"out\":\"test-file.zip\"}",
"FDM": "https://example.com/file.zip"
},
"supportedClients": {
"curl": "cURL 命令",
"wget": "wget 命令",
"aria2": "Aria2",
"idm": "IDM",
"thunder": "迅雷",
"bitcomet": "比特彗星",
"motrix": "Motrix",
"fdm": "Free Download Manager",
"powershell": "PowerShell"
},
"parserInfo": "百度网盘 - pan"
}
```
### 2. 获取指定类型的客户端下载链接
**端点**: `GET /v2/clientLink`
**参数**:
- `url` (必需): 分享链接
- `pwd` (可选): 提取码
- `clientType` (必需): 客户端类型 (curl, wget, aria2, idm, thunder, bitcomet, motrix, fdm, powershell)
**响应**: 直接返回指定类型的客户端下载链接字符串
## 支持的客户端类型
| 客户端类型 | 代码 | 说明 | 输出格式 |
|-----------|------|------|----------|
| cURL | `curl` | 命令行工具 | curl 命令 |
| wget | `wget` | 命令行工具 | wget 命令 |
| Aria2 | `aria2` | 命令行/RPC | aria2c 命令 |
| IDM | `idm` | Windows 下载管理器 | idm:// 协议链接 |
| 迅雷 | `thunder` | 国内主流下载工具 | thunder:// 协议链接 |
| 比特彗星 | `bitcomet` | BT 下载工具 | bitcomet:// 协议链接 |
| Motrix | `motrix` | 跨平台下载工具 | JSON 格式 |
| FDM | `fdm` | Free Download Manager | 文本格式 |
| PowerShell | `powershell` | Windows PowerShell | PowerShell 命令 |
## 使用示例
### 获取所有客户端链接
```bash
curl "http://localhost:8080/v2/clientLinks?url=https://pan.baidu.com/s/1test123&pwd=1234"
```
### 获取 cURL 命令
```bash
curl "http://localhost:8080/v2/clientLink?url=https://pan.baidu.com/s/1test123&pwd=1234&clientType=curl"
```
### 获取 PowerShell 命令
```bash
curl "http://localhost:8080/v2/clientLink?url=https://pan.baidu.com/s/1test123&pwd=1234&clientType=powershell"
```
## 错误处理
当请求失败时API 会返回错误信息:
```json
{
"success": false,
"error": "解析分享链接失败: 具体错误信息"
}
```
## 注意事项
1. **Referer 支持**: CowTool (奶牛快传) 解析器已正确实现 Referer 请求头支持
2. **请求头处理**: 所有客户端链接都会包含必要的请求头(如 User-Agent、Referer、Cookie 等)
3. **特殊字符转义**: PowerShell 命令会自动转义特殊字符(引号、美元符号等)
4. **异步处理**: API 使用异步处理,确保高性能
5. **错误容错**: 即使某个客户端类型生成失败,其他类型仍会正常生成
## 集成说明
该功能已集成到现有的解析器框架中:
- **ParserApi**: 新增两个 API 端点
- **ClientLinkResp**: 新的响应模型
- **CowTool**: 已支持 Referer 请求头
- **PowerShell**: 新增 PowerShell 格式支持
所有功能都经过测试验证,可以安全使用。

View File

@@ -6,11 +6,13 @@ import cn.qaiu.entity.ShareLinkInfo;
import cn.qaiu.lz.common.cache.CacheManager;
import cn.qaiu.lz.common.util.URLParamUtil;
import cn.qaiu.lz.web.model.CacheLinkInfo;
import cn.qaiu.lz.web.model.ClientLinkResp;
import cn.qaiu.lz.web.model.LinkInfoResp;
import cn.qaiu.lz.web.model.StatisticsInfo;
import cn.qaiu.lz.web.service.DbService;
import cn.qaiu.parser.PanDomainTemplate;
import cn.qaiu.parser.ParserCreate;
import cn.qaiu.parser.clientlink.ClientLinkType;
import cn.qaiu.vx.core.annotaions.RouteHandler;
import cn.qaiu.vx.core.annotaions.RouteMapping;
import cn.qaiu.vx.core.enums.RouteMethod;
@@ -244,4 +246,151 @@ public class ParserApi {
.replace("-", "")
.replace(":", "");
}
/**
* 获取客户端下载链接
*
* @param request HTTP请求
* @param pwd 提取码
* @return 客户端下载链接响应
*/
@RouteMapping(value = "/clientLinks", method = RouteMethod.GET)
public Future<ClientLinkResp> getClientLinks(HttpServerRequest request, String pwd) {
Promise<ClientLinkResp> promise = Promise.promise();
try {
String shareUrl = URLParamUtil.parserParams(request);
ParserCreate parserCreate = ParserCreate.fromShareUrl(shareUrl).setShareLinkInfoPwd(pwd);
ShareLinkInfo shareLinkInfo = parserCreate.getShareLinkInfo();
// 使用默认方法解析并生成客户端链接
parserCreate.createTool().parseWithClientLinks()
.onSuccess(clientLinks -> {
try {
ClientLinkResp response = buildClientLinkResponse(shareLinkInfo, clientLinks);
promise.complete(response);
} catch (Exception e) {
log.error("处理客户端链接结果失败", e);
promise.fail(new RuntimeException("处理客户端链接结果失败: " + e.getMessage()));
}
})
.onFailure(error -> {
log.error("解析分享链接失败", error);
promise.fail(new RuntimeException("解析分享链接失败: " + error.getMessage()));
});
} catch (Exception e) {
log.error("解析请求参数失败", e);
promise.fail(new RuntimeException("解析请求参数失败: " + e.getMessage()));
}
return promise.future();
}
/**
* 获取指定类型的客户端下载链接
*
* @param request HTTP请求
* @param pwd 提取码
* @param clientType 客户端类型 (curl, wget, aria2, idm, thunder, bitcomet, motrix, fdm, powershell)
* @return 指定类型的客户端下载链接
*/
@RouteMapping(value = "/clientLink", method = RouteMethod.GET)
public Future<String> getClientLink(HttpServerRequest request, String pwd, String clientType) {
Promise<String> promise = Promise.promise();
try {
String shareUrl = URLParamUtil.parserParams(request);
ParserCreate parserCreate = ParserCreate.fromShareUrl(shareUrl).setShareLinkInfoPwd(pwd);
// 使用默认方法解析并生成客户端链接
parserCreate.createTool().parseWithClientLinks()
.onSuccess(clientLinks -> {
try {
String clientLink = extractClientLinkByType(clientLinks, clientType);
if (clientLink != null) {
promise.complete(clientLink);
} else {
promise.fail("无法生成 " + clientType + " 格式的下载链接");
}
} catch (IllegalArgumentException e) {
promise.fail("不支持的客户端类型: " + clientType);
} catch (Exception e) {
log.error("获取客户端链接失败", e);
promise.fail("获取客户端链接失败: " + e.getMessage());
}
})
.onFailure(error -> {
log.error("解析分享链接失败", error);
promise.fail("解析分享链接失败: " + error.getMessage());
});
} catch (Exception e) {
log.error("解析请求参数失败", e);
promise.fail("解析请求参数失败: " + e.getMessage());
}
return promise.future();
}
/**
* 构建客户端链接响应
*
* @param shareLinkInfo 分享链接信息
* @param clientLinks 客户端链接映射
* @return 客户端链接响应
*/
private ClientLinkResp buildClientLinkResponse(ShareLinkInfo shareLinkInfo, Map<ClientLinkType, String> clientLinks) {
// 从 otherParam 中获取直链
String directLink = (String) shareLinkInfo.getOtherParam().get("downloadUrl");
Map<String, String> supportedClients = buildSupportedClientsMap();
FileInfo fileInfo = extractFileInfo(shareLinkInfo);
return ClientLinkResp.builder()
.success(true)
.directLink(directLink)
.fileName(fileInfo != null ? fileInfo.getFileName() : null)
.fileSize(fileInfo != null ? fileInfo.getSize() : null)
.clientLinks(clientLinks)
.supportedClients(supportedClients)
.parserInfo(shareLinkInfo.getPanName() + " - " + shareLinkInfo.getType())
.build();
}
/**
* 构建支持的客户端类型映射
*
* @return 客户端类型映射
*/
private Map<String, String> buildSupportedClientsMap() {
Map<String, String> supportedClients = new HashMap<>();
for (ClientLinkType type : ClientLinkType.values()) {
supportedClients.put(type.getCode(), type.getDisplayName());
}
return supportedClients;
}
/**
* 从ShareLinkInfo中提取文件信息
*
* @param shareLinkInfo 分享链接信息
* @return 文件信息如果不存在则返回null
*/
private FileInfo extractFileInfo(ShareLinkInfo shareLinkInfo) {
Object fileInfo = shareLinkInfo.getOtherParam().get("fileInfo");
return fileInfo instanceof FileInfo ? (FileInfo) fileInfo : null;
}
/**
* 根据客户端类型提取对应的客户端链接
*
* @param clientLinks 客户端链接映射
* @param clientType 客户端类型
* @return 客户端链接如果不存在则返回null
* @throws IllegalArgumentException 如果客户端类型不支持
*/
private String extractClientLinkByType(Map<ClientLinkType, String> clientLinks, String clientType) {
ClientLinkType type = ClientLinkType.valueOf(clientType.toUpperCase());
return clientLinks.get(type);
}
}

View File

@@ -0,0 +1,62 @@
package cn.qaiu.lz.web.model;
import cn.qaiu.parser.clientlink.ClientLinkType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* 客户端下载链接响应模型
*
* @author <a href="https://qaiu.top">QAIU</a>
* Create at 2025/01/21
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ClientLinkResp {
/**
* 是否成功
*/
private boolean success;
/**
* 错误信息
*/
private String error;
/**
* 直链URL
*/
private String directLink;
/**
* 文件名
*/
private String fileName;
/**
* 文件大小
*/
private Long fileSize;
/**
* 所有客户端下载链接
*/
private Map<ClientLinkType, String> clientLinks;
/**
* 支持的客户端类型列表
*/
private Map<String, String> supportedClients;
/**
* 解析信息
*/
private String parserInfo;
}

View File

@@ -0,0 +1,51 @@
### 客户端下载链接 API 测试
### 环境变量
@host = http://localhost:6400
@testUrl = https://www.kdocs.cn/l/ck0azivLlDi3
@testPwd =
@cowUrl = https://cowtransfer.com/s/test123
@lanzouUrl = https://wwsd.lanzoue.com/iLany1e9bbbi
### 1. 获取所有客户端下载链接
GET {{host}}/v2/clientLinks?url={{lanzouUrl}}&pwd={{testPwd}}
### 2. 获取指定类型的客户端下载链接 - cURL
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=curl
### 3. 获取指定类型的客户端下载链接 - PowerShell
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=powershell
### 4. 获取指定类型的客户端下载链接 - Aria2
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=aria2
### 5. 获取指定类型的客户端下载链接 - 迅雷
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=thunder
### 6. 获取指定类型的客户端下载链接 - IDM
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=idm
### 7. 获取指定类型的客户端下载链接 - wget
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=wget
### 8. 获取指定类型的客户端下载链接 - 比特彗星
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=bitcomet
### 9. 获取指定类型的客户端下载链接 - Motrix
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=motrix
### 10. 获取指定类型的客户端下载链接 - FDM
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=fdm
### 11. 测试不支持的客户端类型
GET {{host}}/v2/clientLink?url={{testUrl}}&pwd={{testPwd}}&clientType=invalid
### 12. 测试奶牛快传(需要 Referer
GET {{host}}/v2/clientLinks?url={{cowUrl}}&pwd=
### 13. 测试空参数
GET {{host}}/v2/clientLinks
### 14. 测试无效URL
GET {{host}}/v2/clientLinks?url=invalid-url&pwd=1234