mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-06-15 01:47:28 +00:00
feat: 乐云 directDownload 接口支持 & 缓存配置补充完善
- 新增 directDownload (GET) 接口,比 packageDownloadWithFileIds 少一次请求 - 每次随机选择下载方式,失败自动 fallback 到另一种 - 统一所有下载方法的 Promise 参数传递 - 添加 HTTP 状态码日志便于调试 - 优化 app-dev.yml 缓存配置注释,补充所有缺失的网盘类型
This commit is contained in:
@@ -15,6 +15,7 @@ import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -24,6 +25,7 @@ public class LeTool extends PanBase {
|
||||
private static final String API_URL_PREFIX = "https://lecloud.lenovo.com/mshare/api/clouddiskapi/share/public/v1/";
|
||||
private static final String DEFAULT_FILE_TYPE = "file";
|
||||
private static final int FILE_TYPE_DIRECTORY = 0; // 目录类型
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private static final MultiMap HEADERS;
|
||||
|
||||
@@ -100,8 +102,8 @@ public class LeTool extends PanBase {
|
||||
}
|
||||
|
||||
String fileId = fileInfoJson.getString("fileId");
|
||||
// 根据文件ID获取跳转链接
|
||||
getDownURL(dataKey, fileId);
|
||||
// 根据文件ID获取跳转链接(随机选择方式,失败自动fallback)
|
||||
getDownURLWithFallback(dataKey, fileId);
|
||||
}
|
||||
} else {
|
||||
fail("{}: {}", resJson.getString("errcode"), resJson.getString("errmsg"));
|
||||
@@ -260,8 +262,8 @@ public class LeTool extends PanBase {
|
||||
String shareId = paramJson.getString("shareId");
|
||||
String fileId = paramJson.getString("fileId");
|
||||
|
||||
// 调用获取下载链接
|
||||
getDownURLForById(shareId, fileId, parsePromise);
|
||||
// 调用获取下载链接(随机选择方式,失败自动fallback)
|
||||
getDownURLWithFallbackForById(shareId, fileId, parsePromise);
|
||||
|
||||
} catch (Exception e) {
|
||||
parsePromise.fail("解析参数失败: " + e.getMessage());
|
||||
@@ -304,14 +306,22 @@ public class LeTool extends PanBase {
|
||||
}).onFailure(err -> promise.fail(err));
|
||||
}
|
||||
|
||||
private void getDownURL(String key, String fileId) {
|
||||
/**
|
||||
* 通过 packageDownloadWithFileIds 接口获取下载链接
|
||||
* 需要两步:先获取 downloadUrl,再请求 302 跳转
|
||||
*
|
||||
* @param shareId 分享ID
|
||||
* @param fileId 文件ID
|
||||
* @param promise 完成时会写入此 promise
|
||||
*/
|
||||
private void getDownURL(String shareId, String fileId, Promise<String> promise) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
JsonArray fileIds = JsonArray.of(fileId);
|
||||
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
|
||||
String apiUrl = API_URL_PREFIX + "packageDownloadWithFileIds";
|
||||
// {"fileIds":[123],"shareId":"xxx","browserId":"uuid"}
|
||||
client.postAbs(apiUrl2)
|
||||
client.postAbs(apiUrl)
|
||||
.putHeaders(HEADERS)
|
||||
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", key, "browserId", uuid))
|
||||
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", shareId, "browserId", uuid))
|
||||
.onSuccess(res -> {
|
||||
JsonObject resJson = asJson(res);
|
||||
if (resJson.containsKey("result")) {
|
||||
@@ -320,20 +330,107 @@ public class LeTool extends PanBase {
|
||||
// 获取重定向链接跳转链接
|
||||
String downloadUrl = dataJson.getString("downloadUrl");
|
||||
if (downloadUrl == null) {
|
||||
fail("Result JSON数据异常: downloadUrl不存在");
|
||||
promise.fail("Result JSON数据异常: downloadUrl不存在");
|
||||
return;
|
||||
}
|
||||
// 获取重定向链接跳转链接
|
||||
clientNoRedirects.getAbs(downloadUrl).send()
|
||||
.onSuccess(res2 -> promise.complete(res2.headers().get("Location")))
|
||||
.onFailure(handleFail(downloadUrl));
|
||||
.onFailure(err -> promise.fail(err));
|
||||
} else {
|
||||
fail("{}: {}", resJson.getString("errcode"), resJson.getString("errmsg"));
|
||||
promise.fail(resJson.getString("errcode") + ": " + resJson.getString("errmsg"));
|
||||
}
|
||||
} else {
|
||||
fail("Result JSON数据异常: result字段不存在");
|
||||
promise.fail("Result JSON数据异常: result字段不存在");
|
||||
}
|
||||
}).onFailure(handleFail(apiUrl2));
|
||||
}).onFailure(err -> promise.fail(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 directDownload 接口获取下载链接
|
||||
* 相比 packageDownloadWithFileIds 少一次请求,直接返回302
|
||||
*
|
||||
* @param shareId 分享ID
|
||||
* @param fileId 文件ID
|
||||
* @param promise 完成时会写入此 promise
|
||||
*/
|
||||
private void getDownURLDirect(String shareId, String fileId, Promise<String> promise) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String apiUrl = API_URL_PREFIX + "directDownload"
|
||||
+ "?shareId=" + shareId
|
||||
+ "&fileId=" + fileId
|
||||
+ "&browserId=" + uuid;
|
||||
|
||||
clientNoRedirects.getAbs(apiUrl)
|
||||
.putHeaders(HEADERS)
|
||||
.send()
|
||||
.onSuccess(res -> {
|
||||
String location = res.headers().get("Location");
|
||||
if (location != null && !location.isEmpty()) {
|
||||
promise.complete(location);
|
||||
} else {
|
||||
log.warn("directDownload 返回非302响应: shareId={}, fileId={}, statusCode={}", shareId, fileId, res.statusCode());
|
||||
promise.fail("directDownload 未返回有效的 Location, statusCode=" + res.statusCode());
|
||||
}
|
||||
})
|
||||
.onFailure(err -> {
|
||||
log.warn("directDownload 请求失败: shareId={}, fileId={}, error={}", shareId, fileId, err.getMessage());
|
||||
promise.fail(err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机选择下载方式并带 fallback(用于 parse)
|
||||
* 先随机选择 directDownload 或 packageDownloadWithFileIds,失败则尝试另一个
|
||||
*/
|
||||
private void getDownURLWithFallback(String shareId, String fileId) {
|
||||
boolean useDirect = RANDOM.nextBoolean();
|
||||
log.info("乐云下载方式选择: shareId={}, fileId={}, method={}", shareId, fileId, useDirect ? "directDownload" : "packageDownloadWithFileIds");
|
||||
|
||||
Promise<String> fallbackPromise = Promise.promise();
|
||||
fallbackPromise.future().onSuccess(url -> {
|
||||
promise.complete(url);
|
||||
}).onFailure(err -> {
|
||||
log.warn("乐云第一种下载方式失败,尝试另一种: {}", err.getMessage());
|
||||
if (useDirect) {
|
||||
getDownURL(shareId, fileId, promise);
|
||||
} else {
|
||||
getDownURLDirect(shareId, fileId, promise);
|
||||
}
|
||||
});
|
||||
|
||||
if (useDirect) {
|
||||
getDownURLDirect(shareId, fileId, fallbackPromise);
|
||||
} else {
|
||||
getDownURL(shareId, fileId, fallbackPromise);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机选择下载方式并带 fallback(用于 parseById)
|
||||
* 先随机选择 directDownload 或 packageDownloadWithFileIds,失败则尝试另一个
|
||||
*/
|
||||
private void getDownURLWithFallbackForById(String shareId, String fileId, Promise<String> promise) {
|
||||
boolean useDirect = RANDOM.nextBoolean();
|
||||
log.info("乐云下载方式选择(parseById): shareId={}, fileId={}, method={}", shareId, fileId, useDirect ? "directDownload" : "packageDownloadWithFileIds");
|
||||
|
||||
Promise<String> fallbackPromise = Promise.promise();
|
||||
fallbackPromise.future().onSuccess(url -> {
|
||||
promise.complete(url);
|
||||
}).onFailure(err -> {
|
||||
log.warn("乐云第一种下载方式失败,尝试另一种: {}", err.getMessage());
|
||||
if (useDirect) {
|
||||
getDownURLForById(shareId, fileId, promise);
|
||||
} else {
|
||||
getDownURLDirect(shareId, fileId, promise);
|
||||
}
|
||||
});
|
||||
|
||||
if (useDirect) {
|
||||
getDownURLDirect(shareId, fileId, fallbackPromise);
|
||||
} else {
|
||||
getDownURLForById(shareId, fileId, fallbackPromise);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,28 +74,54 @@ cache:
|
||||
type: h2db
|
||||
# 默认时长: 单位分钟,大部分网盘未严格验证,建议不要太大
|
||||
defaultDuration: 5
|
||||
# 具体网盘的缓存配置,如果不加配置则不缓存,每次请求都会请求网盘API,格式:网盘标识: 时长
|
||||
# 具体网盘的缓存配置(单位:分钟)
|
||||
# - 配置 key 且有值(如 le: 2879):使用指定时长
|
||||
# - 配置 key 但无值(如 fc:):使用上面的 defaultDuration
|
||||
# - 未配置的 key:不缓存,每次都请求网盘API
|
||||
# 格式:网盘标识: 时长
|
||||
duration:
|
||||
ce: 5
|
||||
cow: 5
|
||||
ec: 5
|
||||
fc:
|
||||
fj: 20
|
||||
iz: 20
|
||||
le: 2879
|
||||
lz: 20
|
||||
qq: 9999999
|
||||
qqw: 30
|
||||
ws: 10
|
||||
ye: -1
|
||||
mne: 30
|
||||
mqq: 30
|
||||
mkg: 30
|
||||
p115: 30
|
||||
ct: 30
|
||||
qishui_music: 5
|
||||
baidu_photo: 5
|
||||
migu: 5
|
||||
# ---- 网盘类 ----
|
||||
ce: 5 # Cloudreve
|
||||
cow: 5 # 奶牛快传
|
||||
ct: 30 # 城通网盘
|
||||
ec: 5 # 移动云空间
|
||||
fc: # 亿方云
|
||||
fj: 20 # 小飞机网盘
|
||||
fs: # 飞书云盘
|
||||
iz: 20 # 蓝奏云优享
|
||||
kd: # 可道云
|
||||
le: 2879 # 联想乐云
|
||||
lz: 20 # 蓝奏云
|
||||
other: # 其他网盘
|
||||
p115: 30 # 115网盘
|
||||
pdb: # Dropbox
|
||||
pcx: # 超星云盘(需要 referer 头)
|
||||
pgd: # Google Drive
|
||||
pic: # iCloud
|
||||
pod: # OneDrive
|
||||
pvyy: # 微雨云存储
|
||||
pwps: # WPS云文档
|
||||
qk: # 夸克网盘
|
||||
qq: 9999999 # QQ邮箱中转站 (iwx.mail.qq.com/ftn/download)
|
||||
qqsc: # QQ闪传 (qfile.qq.com)
|
||||
qqw: 30 # QQ邮箱云盘 (wx.mail.qq.com/s)
|
||||
uc: # UC网盘
|
||||
ws: 10 # 文叔叔
|
||||
ye: -1 # 123网盘
|
||||
|
||||
# ---- 音乐类 ----
|
||||
baidu_photo: 5 # 百度网盘相册
|
||||
migu: 5 # 咪咕音乐
|
||||
mkg: 30 # 酷狗音乐
|
||||
mkgs: # 酷狗音乐分享短链
|
||||
mkgs2: # 酷狗音乐分享2(share/*.html)
|
||||
mkws: # 酷我音乐分享
|
||||
mmgs: # 咪咕音乐分享短链
|
||||
mne: 30 # 网易云音乐
|
||||
mnes: # 网易云音乐分享短链
|
||||
mqq: 30 # QQ音乐
|
||||
mqqs: # QQ音乐分享短链
|
||||
qishui_music: 5 # 汽水音乐
|
||||
|
||||
# httpClient静态代理服务器配置(外网代理)
|
||||
proxy:
|
||||
|
||||
Reference in New Issue
Block a user