mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 12:23:03 +00:00
1. 添加联想乐云直链解析 2. 优化代码逻辑
This commit is contained in:
@@ -16,6 +16,7 @@ public interface IPanTool {
|
||||
case "ye" -> new YeTool(key, pwd);
|
||||
case "fj" -> new FjTool(key, pwd);
|
||||
case "qk" -> new QkTool(key, pwd);
|
||||
case "le" -> new LeTool(key, pwd);
|
||||
default -> {
|
||||
throw new UnsupportedOperationException("未知分享类型");
|
||||
}
|
||||
@@ -38,6 +39,8 @@ public interface IPanTool {
|
||||
return new FjTool(url, pwd);
|
||||
} else if (url.contains(LzTool.LINK_KEY)) {
|
||||
return new LzTool(url, pwd);
|
||||
} else if (url.startsWith(LeTool.SHARE_URL_PREFIX)) {
|
||||
return new LeTool(url, pwd);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("未知分享类型");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import cn.qaiu.WebClientVertxInit;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
@@ -8,24 +9,61 @@ import io.vertx.ext.web.client.WebClientOptions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 解析器抽象类包含promise, HTTP Client, 默认失败方法等;
|
||||
* 新增网盘解析器需要继承该类.
|
||||
*/
|
||||
public abstract class PanBase {
|
||||
protected Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
protected Promise<String> promise = Promise.promise();
|
||||
|
||||
/**
|
||||
* Http client
|
||||
*/
|
||||
protected WebClient client = WebClient.create(WebClientVertxInit.get());
|
||||
protected WebClient clientNoRedirects = WebClient.create(WebClientVertxInit.get(), OPTIONS);
|
||||
private static final WebClientOptions OPTIONS = new WebClientOptions().setFollowRedirects(false);
|
||||
|
||||
/**
|
||||
* Http client 不自动跳转
|
||||
*/
|
||||
protected WebClient clientNoRedirects = WebClient.create(WebClientVertxInit.get(),
|
||||
new WebClientOptions().setFollowRedirects(false));
|
||||
|
||||
/**
|
||||
* 分享key 可以是整个URL; 如果是URL实现该类时要
|
||||
* 使用{@link CommonUtils#adaptShortPaths(String urlPrefix, String key)}获取真实的分享key
|
||||
*/
|
||||
protected String key;
|
||||
|
||||
/**
|
||||
* 分享密码
|
||||
*/
|
||||
protected String pwd;
|
||||
|
||||
/**
|
||||
* 子类重写此构造方法不需要添加额外逻辑
|
||||
* 如:
|
||||
* <blockquote><pre>
|
||||
* public XxTool(String key, String pwd) {
|
||||
* super(key, pwd);
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param key 分享key/url
|
||||
* @param pwd 分享密码
|
||||
*/
|
||||
protected PanBase(String key, String pwd) {
|
||||
this.key = key;
|
||||
this.pwd = pwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败时生成异常消息
|
||||
*
|
||||
* @param t 异常实例
|
||||
* @param errorMsg 提示消息
|
||||
* @param args log参数变量
|
||||
*/
|
||||
protected void fail(Throwable t, String errorMsg, Object... args) {
|
||||
try {
|
||||
String s = String.format(errorMsg.replaceAll("\\{}", "%s"), args);
|
||||
@@ -38,6 +76,12 @@ public abstract class PanBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败时生成异常消息
|
||||
*
|
||||
* @param errorMsg 提示消息
|
||||
* @param args log参数变量
|
||||
*/
|
||||
protected void fail(String errorMsg, Object... args) {
|
||||
try {
|
||||
String s = String.format(errorMsg.replaceAll("\\{}", "%s"), args);
|
||||
@@ -50,6 +94,12 @@ public abstract class PanBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成失败Future的处理器
|
||||
*
|
||||
* @param errorMsg 提示消息
|
||||
* @return Handler
|
||||
*/
|
||||
protected Handler<Throwable> handleFail(String errorMsg) {
|
||||
return t -> fail(this.getClass().getSimpleName() + " - 请求异常 {}: -> {}", errorMsg, t.fillInStackTrace());
|
||||
}
|
||||
|
||||
94
parser/src/main/java/cn/qaiu/parser/impl/LeTool.java
Normal file
94
parser/src/main/java/cn/qaiu/parser/impl/LeTool.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* <a href="https://lecloud.lenovo.com/">联想乐云</a>
|
||||
*/
|
||||
public class LeTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://lecloud.lenovo.com/share/";
|
||||
private static final String API_URL_PREFIX = "https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/";
|
||||
|
||||
public LeTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
// {"shareId":"xxx","password":"xxx","directoryId":"-1"}
|
||||
String apiUrl1 = API_URL_PREFIX + "shareInfo";
|
||||
client.postAbs(apiUrl1)
|
||||
.sendJsonObject(JsonObject.of("shareId", dataKey, "password", pwd, "directoryId", -1))
|
||||
.onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
if (resJson.containsKey("result")) {
|
||||
if (resJson.getBoolean("result")) {
|
||||
JsonObject dataJson = resJson.getJsonObject("data");
|
||||
// 密码验证失败
|
||||
if (!dataJson.getBoolean("passwordVerified")) {
|
||||
fail("密码验证失败, 分享key: {}, 密码: {}", dataKey, pwd);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取文件信息
|
||||
JsonArray files = dataJson.getJsonArray("files");
|
||||
if (files == null || files.size() == 0) {
|
||||
fail("Result JSON数据异常: files字段不存在或jsonArray长度为空");
|
||||
return;
|
||||
}
|
||||
JsonObject fileInfoJson = files.getJsonObject(0);
|
||||
if (fileInfoJson != null) {
|
||||
// TODO 文件大小fileSize和文件名fileName
|
||||
Long fileId = fileInfoJson.getLong("fileId");
|
||||
// 根据文件ID获取跳转链接
|
||||
getDownURL(dataKey, fileId);
|
||||
}
|
||||
} else {
|
||||
fail("{}: {}", resJson.getString("errcode"), resJson.getString("errmsg"));
|
||||
}
|
||||
} else {
|
||||
fail("Result JSON数据异常: result字段不存在");
|
||||
}
|
||||
}).onFailure(handleFail(apiUrl1));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private void getDownURL(String key, Long fileId) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
JsonArray fileIds = JsonArray.of(fileId);
|
||||
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
|
||||
// {"fileIds":[123],"shareId":"xxx","browserId":"uuid"}
|
||||
client.postAbs(apiUrl2)
|
||||
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", key, "browserId", uuid))
|
||||
.onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
if (resJson.containsKey("result")) {
|
||||
if (resJson.getBoolean("result")) {
|
||||
JsonObject dataJson = resJson.getJsonObject("data");
|
||||
// 获取重定向链接跳转链接
|
||||
String downloadUrl = dataJson.getString("downloadUrl");
|
||||
if (downloadUrl == null) {
|
||||
fail("Result JSON数据异常: downloadUrl不存在");
|
||||
return;
|
||||
}
|
||||
// 获取重定向链接跳转链接
|
||||
clientNoRedirects.getAbs(downloadUrl).send()
|
||||
.onSuccess(res2 -> promise.complete(res2.headers().get("Location")))
|
||||
.onFailure(handleFail(downloadUrl));
|
||||
} else {
|
||||
fail("{}: {}", resJson.getString("errcode"), resJson.getString("errmsg"));
|
||||
}
|
||||
} else {
|
||||
fail("Result JSON数据异常: result字段不存在");
|
||||
}
|
||||
}).onFailure(handleFail(apiUrl2));
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.JsExecUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
|
||||
@@ -55,7 +54,7 @@ public class LzTool extends PanBase implements IPanTool {
|
||||
jsText = jsText.substring(0, jsText.indexOf("document.getElementById('rpt')"));
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "down_p");
|
||||
getDownURL(promise, sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
return;
|
||||
@@ -75,7 +74,7 @@ public class LzTool extends PanBase implements IPanTool {
|
||||
}
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, null);
|
||||
getDownURL(promise, sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
}
|
||||
@@ -96,7 +95,7 @@ public class LzTool extends PanBase implements IPanTool {
|
||||
return html.substring(startPos, endPos);
|
||||
}
|
||||
|
||||
private void getDownURL(Promise<String> promise, String key, WebClient client, Map<String, ?> signMap) {
|
||||
private void getDownURL(String key, WebClient client, Map<String, ?> signMap) {
|
||||
MultiMap map = MultiMap.caseInsensitiveMultiMap();
|
||||
signMap.forEach((k, v) -> {
|
||||
map.set(k, v.toString());
|
||||
|
||||
@@ -3,7 +3,6 @@ package cn.qaiu.parser.impl;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
|
||||
public class QkTool extends PanBase implements IPanTool {
|
||||
|
||||
@@ -12,8 +11,6 @@ public class QkTool extends PanBase implements IPanTool {
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
Promise<String> promise = Promise.promise();
|
||||
|
||||
promise.complete("https://lz.qaiu.top");
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
79
web-service/src/main/resources/http-tools/pan-le.http
Normal file
79
web-service/src/main/resources/http-tools/pan-le.http
Normal file
@@ -0,0 +1,79 @@
|
||||
// 联想乐云(联想云服务)
|
||||
|
||||
### 第一步 https://lecloud.lenovo.com/share/4DANWdRQsHHyiFB4a
|
||||
POST https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/shareInfo
|
||||
Content-Type:application/json;charset=UTF-8
|
||||
|
||||
{"shareId":"4DANWdRQsHHyiFB4a1","password":"","directoryId":"-1"}
|
||||
|
||||
### res
|
||||
#{
|
||||
# "result": true,
|
||||
# "data": {
|
||||
# "senderUserName": "7362264*****.com",
|
||||
# "validSecondsLeft": -1,
|
||||
# "memo": "",
|
||||
# "passwordVerified": true,
|
||||
# "files": [
|
||||
# {
|
||||
# "fileType": 99,
|
||||
# "fileId": 12298705,
|
||||
# "fileName": "classes.dex",
|
||||
# "fileSize": 8909208
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
#}
|
||||
|
||||
#{
|
||||
# "result": false,
|
||||
# "errcode": "SHARE_ID_INVALID",
|
||||
# "errmsg": "分享ID无效"
|
||||
#}
|
||||
|
||||
###
|
||||
# @name 第二步
|
||||
POST https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/packageDownloadWithFileIds
|
||||
Content-Type:application/json;charset=UTF-8
|
||||
|
||||
{"fileIds":[12299013],"shareId":"2RkKbLP9BrppS9b43","browserId":"asdddsad"}
|
||||
|
||||
###
|
||||
#{
|
||||
# "result": true,
|
||||
# "data": {
|
||||
# "downloadUrl": "https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12299012&dk=b9d2870c001b56f6b23f6036fd049b237b3b3d47c0dbd30513ddaa7137cd64c9&pk=5a69c8b5f3835171b01ad866241de857d02c8b6e4985e51d652ab1bf331a3ae0&pc=true&ot=ali&ob=sync-cloud-disk&ok=649599424036290560.zip&fn=c4droid_aarch64_gcc11_new.zip&ds=140596045&dc=1&bi=asdddsad&ri=&ts=1701156293620&sn=b9dd3816eb1fd7743f5fa904986c15cb36fd9c08b4fbe0d94d198bd2bbf7c5be"
|
||||
# }
|
||||
#}
|
||||
|
||||
#{
|
||||
# "result": false,
|
||||
# "errcode": "FILE_NOT_EXIST",
|
||||
# "errmsg": "文件不存在"
|
||||
#}
|
||||
|
||||
###
|
||||
# @name 第三步
|
||||
# @no-redirect
|
||||
https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12298704&dk=ad0ef0fe93134baf6850294701e96986b1b933231c346589ccd635520908ebec&pk=ef45aa4d25c1dcecb631b3394f51539d59cb35c6a40c3911df8ba431ba2a3244&pc=true&ot=ali&ob=sync-cloud-disk&ok=649593714557087744.dex&fn=classes.dex&ds=8909208&dc=1&bi=18175394-7437-4eca-8c55-40b73fc70186&ri=&ts=1701148198092&sn=30589173efacb5b493fd47e0f134309ab598252951a5a82d8292011e626b5c26
|
||||
|
||||
###
|
||||
https://lecloud4.lenovomm.cn/dlserver/fileman/ali/sync-cloud-disk/649593714557087744.dex?KEY1=7be0212f4bd3155951942d9d62b8dfd9&KEY2=65681948&order=0&uuid=96b1edd21f4e441ca3d305e86ba714c6&cMD5=false&sorder=0&group=&ts=1701148232156&cpn=-1&cid=3b9aca4b3b9aca4c&__bc=10007&__cid=3b9aca4b3b9aca4c&__ip=60.216.19.75&__ept=1&dck=1&fn=classes.dex
|
||||
|
||||
|
||||
### https://lecloud.lenovo.com/share/2RkKbLP9BrppS9b43(密码:ex2b)
|
||||
POST https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/shareInfo
|
||||
Content-Type:application/json;charset=UTF-8
|
||||
|
||||
{"shareId":"2RkKbLP9BrppS9b43","password":"ex2b","directoryId":"-1"}
|
||||
|
||||
### 错误Result
|
||||
#{
|
||||
# "result": true,
|
||||
# "data": {
|
||||
# "senderUserName": "7362264*****.com",
|
||||
# "validSecondsLeft": -1,
|
||||
# "memo": "tttttt",
|
||||
# "passwordVerified": false
|
||||
# }
|
||||
#}
|
||||
@@ -107,9 +107,30 @@ GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.html&
|
||||
### 123
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/zF07Vv-WkHWd.html&pwd=bios
|
||||
|
||||
|
||||
### 联想乐云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://lecloud.lenovo.com/share/4DANWdRQsHHyiFB4a
|
||||
|
||||
### 联想乐云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/le/4DANWdRQsHHyiFB4a
|
||||
|
||||
### 联想乐云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/le/2RkKbLP9BrppS9b43@ex2b
|
||||
|
||||
### 联想乐云
|
||||
GET http://127.0.0.1:6400/json/le/2RkKbLP9BrppS9b43@ex2b
|
||||
|
||||
|
||||
###
|
||||
GET http://127.0.0.1:6400/v2/statisticsInfo
|
||||
|
||||
###
|
||||
POST http://127.0.0.1:6400/v2/login?username=asd
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user