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.ArrayList;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
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 API_URL_PREFIX = "https://lecloud.lenovo.com/mshare/api/clouddiskapi/share/public/v1/";
|
||||||
private static final String DEFAULT_FILE_TYPE = "file";
|
private static final String DEFAULT_FILE_TYPE = "file";
|
||||||
private static final int FILE_TYPE_DIRECTORY = 0; // 目录类型
|
private static final int FILE_TYPE_DIRECTORY = 0; // 目录类型
|
||||||
|
private static final Random RANDOM = new Random();
|
||||||
|
|
||||||
private static final MultiMap HEADERS;
|
private static final MultiMap HEADERS;
|
||||||
|
|
||||||
@@ -100,8 +102,8 @@ public class LeTool extends PanBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String fileId = fileInfoJson.getString("fileId");
|
String fileId = fileInfoJson.getString("fileId");
|
||||||
// 根据文件ID获取跳转链接
|
// 根据文件ID获取跳转链接(随机选择方式,失败自动fallback)
|
||||||
getDownURL(dataKey, fileId);
|
getDownURLWithFallback(dataKey, fileId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fail("{}: {}", resJson.getString("errcode"), resJson.getString("errmsg"));
|
fail("{}: {}", resJson.getString("errcode"), resJson.getString("errmsg"));
|
||||||
@@ -260,8 +262,8 @@ public class LeTool extends PanBase {
|
|||||||
String shareId = paramJson.getString("shareId");
|
String shareId = paramJson.getString("shareId");
|
||||||
String fileId = paramJson.getString("fileId");
|
String fileId = paramJson.getString("fileId");
|
||||||
|
|
||||||
// 调用获取下载链接
|
// 调用获取下载链接(随机选择方式,失败自动fallback)
|
||||||
getDownURLForById(shareId, fileId, parsePromise);
|
getDownURLWithFallbackForById(shareId, fileId, parsePromise);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
parsePromise.fail("解析参数失败: " + e.getMessage());
|
parsePromise.fail("解析参数失败: " + e.getMessage());
|
||||||
@@ -304,14 +306,22 @@ public class LeTool extends PanBase {
|
|||||||
}).onFailure(err -> promise.fail(err));
|
}).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();
|
String uuid = UUID.randomUUID().toString();
|
||||||
JsonArray fileIds = JsonArray.of(fileId);
|
JsonArray fileIds = JsonArray.of(fileId);
|
||||||
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
|
String apiUrl = API_URL_PREFIX + "packageDownloadWithFileIds";
|
||||||
// {"fileIds":[123],"shareId":"xxx","browserId":"uuid"}
|
// {"fileIds":[123],"shareId":"xxx","browserId":"uuid"}
|
||||||
client.postAbs(apiUrl2)
|
client.postAbs(apiUrl)
|
||||||
.putHeaders(HEADERS)
|
.putHeaders(HEADERS)
|
||||||
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", key, "browserId", uuid))
|
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", shareId, "browserId", uuid))
|
||||||
.onSuccess(res -> {
|
.onSuccess(res -> {
|
||||||
JsonObject resJson = asJson(res);
|
JsonObject resJson = asJson(res);
|
||||||
if (resJson.containsKey("result")) {
|
if (resJson.containsKey("result")) {
|
||||||
@@ -320,20 +330,107 @@ public class LeTool extends PanBase {
|
|||||||
// 获取重定向链接跳转链接
|
// 获取重定向链接跳转链接
|
||||||
String downloadUrl = dataJson.getString("downloadUrl");
|
String downloadUrl = dataJson.getString("downloadUrl");
|
||||||
if (downloadUrl == null) {
|
if (downloadUrl == null) {
|
||||||
fail("Result JSON数据异常: downloadUrl不存在");
|
promise.fail("Result JSON数据异常: downloadUrl不存在");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 获取重定向链接跳转链接
|
// 获取重定向链接跳转链接
|
||||||
clientNoRedirects.getAbs(downloadUrl).send()
|
clientNoRedirects.getAbs(downloadUrl).send()
|
||||||
.onSuccess(res2 -> promise.complete(res2.headers().get("Location")))
|
.onSuccess(res2 -> promise.complete(res2.headers().get("Location")))
|
||||||
.onFailure(handleFail(downloadUrl));
|
.onFailure(err -> promise.fail(err));
|
||||||
} else {
|
} else {
|
||||||
fail("{}: {}", resJson.getString("errcode"), resJson.getString("errmsg"));
|
promise.fail(resJson.getString("errcode") + ": " + resJson.getString("errmsg"));
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
type: h2db
|
||||||
# 默认时长: 单位分钟,大部分网盘未严格验证,建议不要太大
|
# 默认时长: 单位分钟,大部分网盘未严格验证,建议不要太大
|
||||||
defaultDuration: 5
|
defaultDuration: 5
|
||||||
# 具体网盘的缓存配置,如果不加配置则不缓存,每次请求都会请求网盘API,格式:网盘标识: 时长
|
# 具体网盘的缓存配置(单位:分钟)
|
||||||
|
# - 配置 key 且有值(如 le: 2879):使用指定时长
|
||||||
|
# - 配置 key 但无值(如 fc:):使用上面的 defaultDuration
|
||||||
|
# - 未配置的 key:不缓存,每次都请求网盘API
|
||||||
|
# 格式:网盘标识: 时长
|
||||||
duration:
|
duration:
|
||||||
ce: 5
|
# ---- 网盘类 ----
|
||||||
cow: 5
|
ce: 5 # Cloudreve
|
||||||
ec: 5
|
cow: 5 # 奶牛快传
|
||||||
fc:
|
ct: 30 # 城通网盘
|
||||||
fj: 20
|
ec: 5 # 移动云空间
|
||||||
iz: 20
|
fc: # 亿方云
|
||||||
le: 2879
|
fj: 20 # 小飞机网盘
|
||||||
lz: 20
|
fs: # 飞书云盘
|
||||||
qq: 9999999
|
iz: 20 # 蓝奏云优享
|
||||||
qqw: 30
|
kd: # 可道云
|
||||||
ws: 10
|
le: 2879 # 联想乐云
|
||||||
ye: -1
|
lz: 20 # 蓝奏云
|
||||||
mne: 30
|
other: # 其他网盘
|
||||||
mqq: 30
|
p115: 30 # 115网盘
|
||||||
mkg: 30
|
pdb: # Dropbox
|
||||||
p115: 30
|
pcx: # 超星云盘(需要 referer 头)
|
||||||
ct: 30
|
pgd: # Google Drive
|
||||||
qishui_music: 5
|
pic: # iCloud
|
||||||
baidu_photo: 5
|
pod: # OneDrive
|
||||||
migu: 5
|
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静态代理服务器配置(外网代理)
|
# httpClient静态代理服务器配置(外网代理)
|
||||||
proxy:
|
proxy:
|
||||||
|
|||||||
Reference in New Issue
Block a user