From 733059dc8ea293275944b9de8d9dc300c87b5932 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:50:49 +0000 Subject: [PATCH 1/4] Initial plan From 0877fadcfb53ab65e1443498277f1fe7bc268f17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:53:58 +0000 Subject: [PATCH 2/4] Initial planning for Cloudreve 4.x API support Co-authored-by: qaiu <29825328+qaiu@users.noreply.github.com> --- .../verticle/conf/HttpProxyConfConverter.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 core/src/main/generated/cn/qaiu/vx/core/verticle/conf/HttpProxyConfConverter.java diff --git a/core/src/main/generated/cn/qaiu/vx/core/verticle/conf/HttpProxyConfConverter.java b/core/src/main/generated/cn/qaiu/vx/core/verticle/conf/HttpProxyConfConverter.java new file mode 100644 index 0000000..17b355a --- /dev/null +++ b/core/src/main/generated/cn/qaiu/vx/core/verticle/conf/HttpProxyConfConverter.java @@ -0,0 +1,73 @@ +package cn.qaiu.vx.core.verticle.conf; + +import io.vertx.core.json.JsonObject; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.impl.JsonUtil; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.util.Base64; + +/** + * Converter and mapper for {@link cn.qaiu.vx.core.verticle.conf.HttpProxyConf}. + * NOTE: This class has been automatically generated from the {@link cn.qaiu.vx.core.verticle.conf.HttpProxyConf} original class using Vert.x codegen. + */ +public class HttpProxyConfConverter { + + + private static final Base64.Decoder BASE64_DECODER = JsonUtil.BASE64_DECODER; + private static final Base64.Encoder BASE64_ENCODER = JsonUtil.BASE64_ENCODER; + + static void fromJson(Iterable> json, HttpProxyConf obj) { + for (java.util.Map.Entry member : json) { + switch (member.getKey()) { + case "password": + if (member.getValue() instanceof String) { + obj.setPassword((String)member.getValue()); + } + break; + case "port": + if (member.getValue() instanceof Number) { + obj.setPort(((Number)member.getValue()).intValue()); + } + break; + case "preProxyOptions": + if (member.getValue() instanceof JsonObject) { + obj.setPreProxyOptions(new io.vertx.core.net.ProxyOptions((io.vertx.core.json.JsonObject)member.getValue())); + } + break; + case "timeout": + if (member.getValue() instanceof Number) { + obj.setTimeout(((Number)member.getValue()).intValue()); + } + break; + case "username": + if (member.getValue() instanceof String) { + obj.setUsername((String)member.getValue()); + } + break; + } + } + } + + static void toJson(HttpProxyConf obj, JsonObject json) { + toJson(obj, json.getMap()); + } + + static void toJson(HttpProxyConf obj, java.util.Map json) { + if (obj.getPassword() != null) { + json.put("password", obj.getPassword()); + } + if (obj.getPort() != null) { + json.put("port", obj.getPort()); + } + if (obj.getPreProxyOptions() != null) { + json.put("preProxyOptions", obj.getPreProxyOptions().toJson()); + } + if (obj.getTimeout() != null) { + json.put("timeout", obj.getTimeout()); + } + if (obj.getUsername() != null) { + json.put("username", obj.getUsername()); + } + } +} From f1dd9fc0ee43e5f57a2ba0431ca2cf341dddce42 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:57:41 +0000 Subject: [PATCH 3/4] Add Ce4Tool for Cloudreve 4.x API support and update CeTool with version detection Co-authored-by: qaiu <29825328+qaiu@users.noreply.github.com> --- .../java/cn/qaiu/parser/impl/Ce4Tool.java | 195 ++++++++++++++++++ .../main/java/cn/qaiu/parser/impl/CeTool.java | 145 +++++++++++-- 2 files changed, 320 insertions(+), 20 deletions(-) create mode 100644 parser/src/main/java/cn/qaiu/parser/impl/Ce4Tool.java diff --git a/parser/src/main/java/cn/qaiu/parser/impl/Ce4Tool.java b/parser/src/main/java/cn/qaiu/parser/impl/Ce4Tool.java new file mode 100644 index 0000000..a9cdae5 --- /dev/null +++ b/parser/src/main/java/cn/qaiu/parser/impl/Ce4Tool.java @@ -0,0 +1,195 @@ +package cn.qaiu.parser.impl; + +import cn.qaiu.entity.ShareLinkInfo; +import cn.qaiu.parser.PanBase; +import io.vertx.core.Future; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.client.HttpRequest; + +import java.net.URL; + +/** + * Cloudreve 4.x 自建网盘解析
+ * Cloudreve 4.x API 版本解析器 + */ +public class Ce4Tool extends PanBase { + + // Cloudreve 4.x uses /api/v3/ prefix for most APIs + private static final String PING_API_V3_PATH = "/api/v3/site/ping"; + private static final String PING_API_V4_PATH = "/api/v4/site/ping"; + private static final String FILE_URL_API_PATH = "/api/v3/file/url"; + private static final String SHARE_API_PATH = "/api/v3/share/info/"; + + public Ce4Tool(ShareLinkInfo shareLinkInfo) { + super(shareLinkInfo); + } + + public Future parse() { + String key = shareLinkInfo.getShareKey(); + String pwd = shareLinkInfo.getSharePassword(); + + try { + URL url = new URL(shareLinkInfo.getShareUrl()); + String baseUrl = url.getProtocol() + "://" + url.getHost(); + + // First, detect API version by pinging + detectVersion(baseUrl, key, pwd); + } catch (Exception e) { + fail(e, "URL解析错误"); + } + return promise.future(); + } + + /** + * Detect Cloudreve version by pinging /api/v3/site/ping or /api/v4/site/ping + */ + private void detectVersion(String baseUrl, String key, String pwd) { + String pingUrlV3 = baseUrl + PING_API_V3_PATH; + + // Try v3 ping first (which also works for 4.x) + clientSession.getAbs(pingUrlV3).send().onSuccess(res -> { + if (res.statusCode() == 200) { + try { + JsonObject pingResponse = asJson(res); + // If we get a valid JSON response, this is a Cloudreve instance + // Check if it's 4.x by trying the share API + getShareInfo(baseUrl, key, pwd); + } catch (Exception e) { + // Not a valid JSON response, try v4 ping + tryV4Ping(baseUrl, key, pwd); + } + } else if (res.statusCode() == 404) { + // Try v4 ping + tryV4Ping(baseUrl, key, pwd); + } else { + // Not a Cloudreve instance, try next parser + nextParser(); + } + }).onFailure(t -> { + // Network error or not accessible, try next parser + nextParser(); + }); + } + + private void tryV4Ping(String baseUrl, String key, String pwd) { + String pingUrlV4 = baseUrl + PING_API_V4_PATH; + + clientSession.getAbs(pingUrlV4).send().onSuccess(res -> { + if (res.statusCode() == 200) { + try { + JsonObject pingResponse = asJson(res); + // Valid v4 response + getShareInfo(baseUrl, key, pwd); + } catch (Exception e) { + // Not a Cloudreve instance + nextParser(); + } + } else { + // Not a Cloudreve instance + nextParser(); + } + }).onFailure(t -> { + // Not accessible, try next parser + nextParser(); + }); + } + + /** + * Get share information from Cloudreve 4.x + */ + private void getShareInfo(String baseUrl, String key, String pwd) { + String shareApiUrl = baseUrl + SHARE_API_PATH + key; + + HttpRequest httpRequest = clientSession.getAbs(shareApiUrl); + if (pwd != null && !pwd.isEmpty()) { + httpRequest.addQueryParam("password", pwd); + } + + httpRequest.send().onSuccess(res -> { + try { + if (res.statusCode() == 200) { + JsonObject jsonObject = asJson(res); + if (jsonObject.containsKey("code")) { + int code = jsonObject.getInteger("code"); + if (code == 0) { + // Success, get file info and download URL + JsonObject data = jsonObject.getJsonObject("data"); + if (data != null) { + // Get file path or use default + String filePath = "/"; + if (data.containsKey("path")) { + filePath = data.getString("path"); + } + // For 4.x, we need to get the download URL via POST /api/v3/file/url + getDownloadUrl(baseUrl, key, filePath); + } else { + fail("分享信息获取失败: data字段为空"); + } + } else { + // Error code, might be wrong password or invalid share + String msg = jsonObject.getString("msg", "未知错误"); + fail("分享验证失败: {}", msg); + } + } else { + // Not a Cloudreve 4.x response, try next parser + nextParser(); + } + } else { + // HTTP error, not a valid Cloudreve instance + nextParser(); + } + } catch (Exception e) { + // JSON parsing error, not a Cloudreve instance + nextParser(); + } + }).onFailure(t -> { + // Network error, try next parser + nextParser(); + }); + } + + /** + * Get download URL via POST /api/v3/file/url (Cloudreve 4.x API) + */ + private void getDownloadUrl(String baseUrl, String key, String filePath) { + String fileUrlApi = baseUrl + FILE_URL_API_PATH; + + // Prepare request body for Cloudreve 4.x + JsonObject requestBody = new JsonObject() + .put("uris", new JsonArray().add(filePath)) + .put("download", true); + + clientSession.postAbs(fileUrlApi) + .putHeader("Content-Type", "application/json") + .sendJsonObject(requestBody) + .onSuccess(res -> { + try { + if (res.statusCode() == 200) { + JsonObject jsonObject = asJson(res); + if (jsonObject.containsKey("urls")) { + JsonArray urls = jsonObject.getJsonArray("urls"); + if (urls != null && urls.size() > 0) { + JsonObject urlObj = urls.getJsonObject(0); + String downloadUrl = urlObj.getString("url"); + if (downloadUrl != null && !downloadUrl.isEmpty()) { + promise.complete(downloadUrl); + } else { + fail("下载链接为空"); + } + } else { + fail("下载链接列表为空"); + } + } else { + fail("响应中不包含urls字段"); + } + } else { + fail("获取下载链接失败: HTTP {}", res.statusCode()); + } + } catch (Exception e) { + fail(e, "解析下载链接响应失败"); + } + }).onFailure(handleFail(fileUrlApi)); + } +} diff --git a/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java b/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java index 968f6cc..460db22 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java @@ -15,6 +15,7 @@ import java.net.URL; * huang1111
* 看见存储
* 亿安云盘
+ * Cloudreve 3.x 解析器,会自动检测版本并在4.x时转发到Ce4Tool */ public class CeTool extends PanBase { @@ -22,6 +23,9 @@ public class CeTool extends PanBase { // api/v3/share/info/g31PcQ?password=qaiu private static final String SHARE_API_PATH = "/api/v3/share/info/"; + + private static final String PING_API_V3_PATH = "/api/v3/site/ping"; + private static final String PING_API_V4_PATH = "/api/v4/site/ping"; public CeTool(ShareLinkInfo shareLinkInfo) { super(shareLinkInfo); @@ -39,31 +43,132 @@ public class CeTool extends PanBase { try { // // 处理URL URL url = new URL(shareLinkInfo.getShareUrl()); - String downloadApiUrl = url.getProtocol() + "://" + url.getHost() + DOWNLOAD_API_PATH + key + "?path" + - "=undefined/undefined;"; - String shareApiUrl = url.getProtocol() + "://" + url.getHost() + SHARE_API_PATH + key; - // 设置cookie - HttpRequest httpRequest = clientSession.getAbs(shareApiUrl); - if (pwd != null) { - httpRequest.addQueryParam("password", pwd); - } - // 获取下载链接 - httpRequest.send().onSuccess(res -> { - try { - if (res.statusCode() == 200 && res.bodyAsJsonObject().containsKey("code")) { - getDownURL(downloadApiUrl); - } else { - nextParser(); - } - } catch (Exception e) { - nextParser(); - } - }).onFailure(handleFail(shareApiUrl)); + String baseUrl = url.getProtocol() + "://" + url.getHost(); + + // 先检测API版本 + detectVersionAndParse(baseUrl, key, pwd); } catch (Exception e) { fail(e, "URL解析错误"); } return promise.future(); } + + /** + * 检测Cloudreve版本并选择合适的解析器 + * 先调用 /api/v3/site/ping 或 /api/v4/site/ping 判断是哪个版本 + * 如果都返回404说明不是Cloudreve盘,则调用nextParser + */ + private void detectVersionAndParse(String baseUrl, String key, String pwd) { + String pingUrlV3 = baseUrl + PING_API_V3_PATH; + + // 先尝试v3 ping + clientSession.getAbs(pingUrlV3).send().onSuccess(res -> { + if (res.statusCode() == 200) { + try { + JsonObject pingResponse = asJson(res); + // 获取到JSON响应,检查是否是4.x版本 + // 4.x的ping响应可能有不同的结构,我们通过share API来判断 + checkVersionByShareApi(baseUrl, key, pwd); + } catch (Exception e) { + // JSON解析失败,尝试v4 ping + tryV4PingAndParse(baseUrl, key, pwd); + } + } else if (res.statusCode() == 404) { + // v3 ping不存在,尝试v4 + tryV4PingAndParse(baseUrl, key, pwd); + } else { + // 其他错误,不是Cloudreve盘 + nextParser(); + } + }).onFailure(t -> { + // 网络错误,尝试下一个解析器 + nextParser(); + }); + } + + private void tryV4PingAndParse(String baseUrl, String key, String pwd) { + String pingUrlV4 = baseUrl + PING_API_V4_PATH; + + clientSession.getAbs(pingUrlV4).send().onSuccess(res -> { + if (res.statusCode() == 200) { + try { + JsonObject pingResponse = asJson(res); + // v4 ping成功,使用Ce4Tool + delegateToCe4Tool(); + } catch (Exception e) { + // 不是Cloudreve盘 + nextParser(); + } + } else { + // 不是Cloudreve盘 + nextParser(); + } + }).onFailure(t -> { + // 网络错误,尝试下一个解析器 + nextParser(); + }); + } + + /** + * 通过Share API的响应来判断版本 + * 3.x和4.x的share API响应格式可能不同 + */ + private void checkVersionByShareApi(String baseUrl, String key, String pwd) { + String shareApiUrl = baseUrl + SHARE_API_PATH + key; + HttpRequest httpRequest = clientSession.getAbs(shareApiUrl); + if (pwd != null) { + httpRequest.addQueryParam("password", pwd); + } + + httpRequest.send().onSuccess(res -> { + try { + if (res.statusCode() == 200 && res.bodyAsJsonObject().containsKey("code")) { + JsonObject jsonObject = asJson(res); + // 检查响应结构来判断版本 + // 如果share API成功,但download API返回404,说明是4.x + // 这里我们先尝试3.x的download API + String downloadApiUrl = baseUrl + DOWNLOAD_API_PATH + key + "?path=undefined/undefined;"; + checkDownloadApi(downloadApiUrl, baseUrl, key, pwd); + } else { + nextParser(); + } + } catch (Exception e) { + nextParser(); + } + }).onFailure(t -> { + nextParser(); + }); + } + + /** + * 检查3.x的download API是否存在 + * 如果不存在,说明是4.x版本 + */ + private void checkDownloadApi(String downloadApiUrl, String baseUrl, String key, String pwd) { + clientSession.putAbs(downloadApiUrl).send().onSuccess(res -> { + if (res.statusCode() == 404 || res.statusCode() == 405) { + // download API不存在或方法不允许,说明是4.x + delegateToCe4Tool(); + } else if (res.statusCode() == 200) { + // 3.x版本,继续使用当前逻辑 + getDownURL(downloadApiUrl); + } else { + // 其他错误 + fail("无法确定Cloudreve版本或接口调用失败"); + } + }).onFailure(t -> { + // 尝试使用4.x + delegateToCe4Tool(); + }); + } + + /** + * 转发到Ce4Tool处理4.x版本 + */ + private void delegateToCe4Tool() { + log.debug("检测到Cloudreve 4.x,转发到Ce4Tool处理"); + new Ce4Tool(shareLinkInfo).parse().onComplete(promise); + } private void getDownURL(String shareApiUrl) { From d23b11577e3a673ee71cd80b4eb85c0644f81b90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:59:48 +0000 Subject: [PATCH 4/4] Simplify and optimize Ce4Tool and CeTool version detection logic Co-authored-by: qaiu <29825328+qaiu@users.noreply.github.com> --- .../java/cn/qaiu/parser/impl/Ce4Tool.java | 99 ++++--------------- .../main/java/cn/qaiu/parser/impl/CeTool.java | 85 +++++++--------- 2 files changed, 53 insertions(+), 131 deletions(-) diff --git a/parser/src/main/java/cn/qaiu/parser/impl/Ce4Tool.java b/parser/src/main/java/cn/qaiu/parser/impl/Ce4Tool.java index a9cdae5..baf5a5b 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/Ce4Tool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/Ce4Tool.java @@ -12,13 +12,12 @@ import java.net.URL; /** * Cloudreve 4.x 自建网盘解析
- * Cloudreve 4.x API 版本解析器 + * Cloudreve 4.x API 版本解析器
+ * 此解析器专门处理Cloudreve 4.x版本的API,使用新的下载流程 */ public class Ce4Tool extends PanBase { // Cloudreve 4.x uses /api/v3/ prefix for most APIs - private static final String PING_API_V3_PATH = "/api/v3/site/ping"; - private static final String PING_API_V4_PATH = "/api/v4/site/ping"; private static final String FILE_URL_API_PATH = "/api/v3/file/url"; private static final String SHARE_API_PATH = "/api/v3/share/info/"; @@ -34,8 +33,8 @@ public class Ce4Tool extends PanBase { URL url = new URL(shareLinkInfo.getShareUrl()); String baseUrl = url.getProtocol() + "://" + url.getHost(); - // First, detect API version by pinging - detectVersion(baseUrl, key, pwd); + // 获取分享信息 + getShareInfo(baseUrl, key, pwd); } catch (Exception e) { fail(e, "URL解析错误"); } @@ -43,61 +42,7 @@ public class Ce4Tool extends PanBase { } /** - * Detect Cloudreve version by pinging /api/v3/site/ping or /api/v4/site/ping - */ - private void detectVersion(String baseUrl, String key, String pwd) { - String pingUrlV3 = baseUrl + PING_API_V3_PATH; - - // Try v3 ping first (which also works for 4.x) - clientSession.getAbs(pingUrlV3).send().onSuccess(res -> { - if (res.statusCode() == 200) { - try { - JsonObject pingResponse = asJson(res); - // If we get a valid JSON response, this is a Cloudreve instance - // Check if it's 4.x by trying the share API - getShareInfo(baseUrl, key, pwd); - } catch (Exception e) { - // Not a valid JSON response, try v4 ping - tryV4Ping(baseUrl, key, pwd); - } - } else if (res.statusCode() == 404) { - // Try v4 ping - tryV4Ping(baseUrl, key, pwd); - } else { - // Not a Cloudreve instance, try next parser - nextParser(); - } - }).onFailure(t -> { - // Network error or not accessible, try next parser - nextParser(); - }); - } - - private void tryV4Ping(String baseUrl, String key, String pwd) { - String pingUrlV4 = baseUrl + PING_API_V4_PATH; - - clientSession.getAbs(pingUrlV4).send().onSuccess(res -> { - if (res.statusCode() == 200) { - try { - JsonObject pingResponse = asJson(res); - // Valid v4 response - getShareInfo(baseUrl, key, pwd); - } catch (Exception e) { - // Not a Cloudreve instance - nextParser(); - } - } else { - // Not a Cloudreve instance - nextParser(); - } - }).onFailure(t -> { - // Not accessible, try next parser - nextParser(); - }); - } - - /** - * Get share information from Cloudreve 4.x + * 获取Cloudreve 4.x分享信息 */ private void getShareInfo(String baseUrl, String key, String pwd) { String shareApiUrl = baseUrl + SHARE_API_PATH + key; @@ -114,49 +59,45 @@ public class Ce4Tool extends PanBase { if (jsonObject.containsKey("code")) { int code = jsonObject.getInteger("code"); if (code == 0) { - // Success, get file info and download URL + // 成功,获取文件信息和下载链接 JsonObject data = jsonObject.getJsonObject("data"); if (data != null) { - // Get file path or use default + // 获取文件路径,如果没有则使用默认路径 String filePath = "/"; if (data.containsKey("path")) { filePath = data.getString("path"); } - // For 4.x, we need to get the download URL via POST /api/v3/file/url - getDownloadUrl(baseUrl, key, filePath); + // 对于4.x,需要通过 POST /api/v3/file/url 获取下载链接 + getDownloadUrl(baseUrl, filePath); } else { fail("分享信息获取失败: data字段为空"); } } else { - // Error code, might be wrong password or invalid share + // 错误码,可能是密码错误或分享失效 String msg = jsonObject.getString("msg", "未知错误"); fail("分享验证失败: {}", msg); } } else { - // Not a Cloudreve 4.x response, try next parser - nextParser(); + // 响应格式不符合预期 + fail("响应格式不符合Cloudreve 4.x规范"); } } else { - // HTTP error, not a valid Cloudreve instance - nextParser(); + // HTTP错误 + fail("获取分享信息失败: HTTP {}", res.statusCode()); } } catch (Exception e) { - // JSON parsing error, not a Cloudreve instance - nextParser(); + fail(e, "解析分享信息响应失败"); } - }).onFailure(t -> { - // Network error, try next parser - nextParser(); - }); + }).onFailure(handleFail(shareApiUrl)); } /** - * Get download URL via POST /api/v3/file/url (Cloudreve 4.x API) + * 通过 POST /api/v3/file/url 获取下载链接 (Cloudreve 4.x API) */ - private void getDownloadUrl(String baseUrl, String key, String filePath) { + private void getDownloadUrl(String baseUrl, String filePath) { String fileUrlApi = baseUrl + FILE_URL_API_PATH; - // Prepare request body for Cloudreve 4.x + // 准备Cloudreve 4.x的请求体 JsonObject requestBody = new JsonObject() .put("uris", new JsonArray().add(filePath)) .put("download", true); @@ -182,7 +123,7 @@ public class Ce4Tool extends PanBase { fail("下载链接列表为空"); } } else { - fail("响应中不包含urls字段"); + fail("响应中不包含urls字段: {}", jsonObject.encodePrettily()); } } else { fail("获取下载链接失败: HTTP {}", res.statusCode()); diff --git a/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java b/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java index 460db22..5a6f4b9 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java @@ -35,13 +35,7 @@ public class CeTool extends PanBase { public Future parse() { String key = shareLinkInfo.getShareKey(); String pwd = shareLinkInfo.getSharePassword(); - // https://pan.huang1111.cn/s/wDz5TK - // https://pan.huang1111.cn/s/y12bI6 -> https://pan.huang1111 - // .cn/api/v3/share/download/y12bI6?path=undefined%2Fundefined; - // 类型解析 -> /ce/pan.huang1111.cn_s_wDz5TK - // parser接口 -> /parser?url=https://pan.huang1111.cn/s/wDz5TK try { -// // 处理URL URL url = new URL(shareLinkInfo.getShareUrl()); String baseUrl = url.getProtocol() + "://" + url.getHost(); @@ -55,8 +49,8 @@ public class CeTool extends PanBase { /** * 检测Cloudreve版本并选择合适的解析器 - * 先调用 /api/v3/site/ping 或 /api/v4/site/ping 判断是哪个版本 - * 如果都返回404说明不是Cloudreve盘,则调用nextParser + * 先调用 /api/v3/site/ping 判断哪个API 如果/v3 或者/v4 能查询到json响应,可以判断是哪个版本 + * 不然返回404说明不是ce盘直接nextParser */ private void detectVersionAndParse(String baseUrl, String key, String pwd) { String pingUrlV3 = baseUrl + PING_API_V3_PATH; @@ -65,42 +59,43 @@ public class CeTool extends PanBase { clientSession.getAbs(pingUrlV3).send().onSuccess(res -> { if (res.statusCode() == 200) { try { - JsonObject pingResponse = asJson(res); - // 获取到JSON响应,检查是否是4.x版本 - // 4.x的ping响应可能有不同的结构,我们通过share API来判断 - checkVersionByShareApi(baseUrl, key, pwd); + asJson(res); + // v3 ping成功,可能是3.x或4.x,尝试3.x的download API来判断 + String shareApiUrl = baseUrl + SHARE_API_PATH + key; + String downloadApiUrl = baseUrl + DOWNLOAD_API_PATH + key + "?path=undefined/undefined;"; + checkIfV3(shareApiUrl, downloadApiUrl, pwd); } catch (Exception e) { // JSON解析失败,尝试v4 ping - tryV4PingAndParse(baseUrl, key, pwd); + tryV4Ping(baseUrl, key, pwd); } } else if (res.statusCode() == 404) { // v3 ping不存在,尝试v4 - tryV4PingAndParse(baseUrl, key, pwd); + tryV4Ping(baseUrl, key, pwd); } else { // 其他错误,不是Cloudreve盘 nextParser(); } }).onFailure(t -> { - // 网络错误,尝试下一个解析器 - nextParser(); + // 网络错误或不可达,尝试v4 ping + tryV4Ping(baseUrl, key, pwd); }); } - private void tryV4PingAndParse(String baseUrl, String key, String pwd) { + private void tryV4Ping(String baseUrl, String key, String pwd) { String pingUrlV4 = baseUrl + PING_API_V4_PATH; clientSession.getAbs(pingUrlV4).send().onSuccess(res -> { if (res.statusCode() == 200) { try { - JsonObject pingResponse = asJson(res); + asJson(res); // v4 ping成功,使用Ce4Tool delegateToCe4Tool(); } catch (Exception e) { - // 不是Cloudreve盘 + // JSON解析失败,不是Cloudreve盘 nextParser(); } } else { - // 不是Cloudreve盘 + // v4 ping失败,不是Cloudreve盘 nextParser(); } }).onFailure(t -> { @@ -110,11 +105,9 @@ public class CeTool extends PanBase { } /** - * 通过Share API的响应来判断版本 - * 3.x和4.x的share API响应格式可能不同 + * 检查是否是3.x版本,通过尝试调用3.x的API */ - private void checkVersionByShareApi(String baseUrl, String key, String pwd) { - String shareApiUrl = baseUrl + SHARE_API_PATH + key; + private void checkIfV3(String shareApiUrl, String downloadApiUrl, String pwd) { HttpRequest httpRequest = clientSession.getAbs(shareApiUrl); if (pwd != null) { httpRequest.addQueryParam("password", pwd); @@ -123,12 +116,22 @@ public class CeTool extends PanBase { httpRequest.send().onSuccess(res -> { try { if (res.statusCode() == 200 && res.bodyAsJsonObject().containsKey("code")) { - JsonObject jsonObject = asJson(res); - // 检查响应结构来判断版本 - // 如果share API成功,但download API返回404,说明是4.x - // 这里我们先尝试3.x的download API - String downloadApiUrl = baseUrl + DOWNLOAD_API_PATH + key + "?path=undefined/undefined;"; - checkDownloadApi(downloadApiUrl, baseUrl, key, pwd); + // share API成功,尝试download API + clientSession.putAbs(downloadApiUrl).send().onSuccess(res2 -> { + if (res2.statusCode() == 200 || res2.statusCode() == 400) { + // 3.x版本的download API存在 + getDownURL(downloadApiUrl); + } else if (res2.statusCode() == 404 || res2.statusCode() == 405) { + // download API不存在,说明是4.x + delegateToCe4Tool(); + } else { + // 其他错误,可能是4.x + delegateToCe4Tool(); + } + }).onFailure(t -> { + // 请求失败,尝试4.x + delegateToCe4Tool(); + }); } else { nextParser(); } @@ -140,28 +143,6 @@ public class CeTool extends PanBase { }); } - /** - * 检查3.x的download API是否存在 - * 如果不存在,说明是4.x版本 - */ - private void checkDownloadApi(String downloadApiUrl, String baseUrl, String key, String pwd) { - clientSession.putAbs(downloadApiUrl).send().onSuccess(res -> { - if (res.statusCode() == 404 || res.statusCode() == 405) { - // download API不存在或方法不允许,说明是4.x - delegateToCe4Tool(); - } else if (res.statusCode() == 200) { - // 3.x版本,继续使用当前逻辑 - getDownURL(downloadApiUrl); - } else { - // 其他错误 - fail("无法确定Cloudreve版本或接口调用失败"); - } - }).onFailure(t -> { - // 尝试使用4.x - delegateToCe4Tool(); - }); - } - /** * 转发到Ce4Tool处理4.x版本 */