1. 添加联想乐云直链解析 2. 优化代码逻辑

This commit is contained in:
qaiu
2023-11-28 16:51:51 +08:00
parent 66e62aead0
commit c958229960
7 changed files with 252 additions and 9 deletions

View File

@@ -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("未知分享类型");

View File

@@ -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());
}

View 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));
}
}

View File

@@ -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());

View File

@@ -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();
}