mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 20:33: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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user