mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-02-03 20:06:18 +00:00
Compare commits
12 Commits
0f28dc2896
...
v019b21b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9fa83e57c | ||
|
|
ba4666c32a | ||
|
|
d6d37e8204 | ||
|
|
cb9dbfcc69 | ||
|
|
42b366ed0f | ||
|
|
4021c507b6 | ||
|
|
4d8e82080d | ||
|
|
fca608b44e | ||
|
|
34cb89a6ea | ||
|
|
69d5f269bd | ||
|
|
459c974cb8 | ||
|
|
2f22cb01eb |
52
.vscode/launch.json
vendored
52
.vscode/launch.json
vendored
@@ -1,7 +1,4 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
@@ -10,61 +7,12 @@
|
||||
"request": "launch",
|
||||
"mainClass": "${file}"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "StringCase",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.vx.core.util.StringCase",
|
||||
"projectName": "core"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "FCURLParser",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.parser.FCURLParser",
|
||||
"projectName": "parser"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "QkTool",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.parser.impl.QkTool",
|
||||
"projectName": "parser"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "WebClientExample",
|
||||
"request": "launch",
|
||||
"mainClass": "qaiu.web.test.WebClientExample",
|
||||
"projectName": "parser"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "AppMain",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.lz.AppMain",
|
||||
"projectName": "web-service"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "TestJs",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.web.test.TestJs",
|
||||
"projectName": "web-service"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "TestOS",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.web.test.TestOS",
|
||||
"projectName": "web-service"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "WebProxyExamples",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.web.test.WebProxyExamples",
|
||||
"projectName": "web-service"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
|
||||
# netdisk-fast-download 网盘分享链接云解析服务
|
||||
QQ群:1017480890
|
||||
QQ交流群:1017480890
|
||||
|
||||
netdisk-fast-download网盘直链云解析(nfd云解析)能把网盘分享下载链接转化为直链,支持多款云盘,已支持蓝奏云/蓝奏云优享/奶牛快传/移动云云空间/小飞机盘/亿方云/123云盘/Cloudreve等,支持加密分享,以及部分网盘文件夹分享。
|
||||
|
||||
@@ -45,7 +45,7 @@ https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.
|
||||
## 预览地址
|
||||
[预览地址1](https://lz.qaiu.top)
|
||||
[预览地址2](https://lz0.qaiu.top)
|
||||
[移动/联通/天翼云盘大文件试用版](https://189.qaiu.top)
|
||||
[天翼云盘/移动云盘限时体验版](https://189.qaiu.top)
|
||||
|
||||
main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/netdisk-fast-download/tree/main-jdk11)
|
||||
**0.1.8及以上版本json接口格式有调整 参考json返回数据格式示例**
|
||||
@@ -95,6 +95,8 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
|
||||
|
||||
## API接口
|
||||
|
||||
[api接口文档](https://nfdparser.apifox.cn/)
|
||||
|
||||
### 服务端口
|
||||
- **6400**: API 服务端口(建议使用 Nginx 代理)
|
||||
- **6401**: 内置 Web 解析工具(个人使用可直接开放此端口)
|
||||
|
||||
@@ -104,7 +104,7 @@ public enum PanDomainTemplate {
|
||||
"lanzoug|" +
|
||||
"lanzoum" +
|
||||
")\\.com/(.+/)?(?<KEY>.+)"),
|
||||
"https://lanzoux.com/{shareKey}",
|
||||
"https://w1.lanzn.com/{shareKey}",
|
||||
LzTool.class),
|
||||
|
||||
// https://www.feijix.com/s/
|
||||
|
||||
@@ -4,13 +4,15 @@ import cn.qaiu.entity.FileInfo;
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.AESUtils;
|
||||
import cn.qaiu.util.AcwScV2Generator;
|
||||
import cn.qaiu.util.FileSizeConverter;
|
||||
import cn.qaiu.util.UUIDUtil;
|
||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClientSession;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -18,10 +20,12 @@ import java.util.*;
|
||||
|
||||
/**
|
||||
* 蓝奏云优享
|
||||
*
|
||||
* v019b22
|
||||
*/
|
||||
public class IzTool extends PanBase {
|
||||
|
||||
WebClientSession webClientSession = WebClientSession.create(clientNoRedirects);
|
||||
|
||||
private static final String API_URL_PREFIX = "https://api.ilanzou.com/unproved/";
|
||||
|
||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" +
|
||||
@@ -70,6 +74,19 @@ public class IzTool extends PanBase {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
private void setCookie(String html) {
|
||||
int beginIndex = html.indexOf("arg1='") + 6;
|
||||
String arg1 = html.substring(beginIndex, html.indexOf("';", beginIndex));
|
||||
String acw_sc__v2 = AcwScV2Generator.acwScV2Simple(arg1);
|
||||
// 创建一个 Cookie 并放入 CookieStore
|
||||
DefaultCookie nettyCookie = new DefaultCookie("acw_sc__v2", acw_sc__v2);
|
||||
nettyCookie.setDomain(".ilanzou.com"); // 设置域名
|
||||
nettyCookie.setPath("/"); // 设置路径
|
||||
nettyCookie.setSecure(false);
|
||||
nettyCookie.setHttpOnly(false);
|
||||
webClientSession.cookieStore().put(nettyCookie);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
String shareId = shareLinkInfo.getShareKey();
|
||||
|
||||
@@ -80,70 +97,100 @@ public class IzTool extends PanBase {
|
||||
// POST https://api.ilanzou.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
||||
String url = StringUtils.isBlank(shareLinkInfo.getSharePassword()) ? FIRST_REQUEST_URL
|
||||
: (FIRST_REQUEST_URL + "&code=" + shareLinkInfo.getSharePassword());
|
||||
client.postAbs(UriTemplate.of(VIP_REQUEST_URL))
|
||||
webClientSession.postAbs(UriTemplate.of(VIP_REQUEST_URL))
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
.send().onSuccess(r0 -> { // 忽略res
|
||||
// 第一次请求 获取文件信息
|
||||
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
||||
client.postAbs(UriTemplate.of(url))
|
||||
webClientSession.postAbs(UriTemplate.of(url))
|
||||
.putHeaders(header)
|
||||
.setTemplateParam("shareId", shareId)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
.send().onSuccess(res -> {
|
||||
JsonObject resJson = asJson(res);
|
||||
if (resJson.getInteger("code") != 200) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
String resBody = asText(res);
|
||||
// 检查是否包含 cookie 验证
|
||||
if (resBody.contains("var arg1='")) {
|
||||
webClientSession = WebClientSession.create(clientNoRedirects);
|
||||
setCookie(resBody);
|
||||
// 重新请求
|
||||
webClientSession.postAbs(UriTemplate.of(url))
|
||||
.putHeaders(header)
|
||||
.setTemplateParam("shareId", shareId)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
.send().onSuccess(res2 -> {
|
||||
handleParseResponse(asText(res2), shareId);
|
||||
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
return;
|
||||
}
|
||||
if (resJson.getJsonArray("list").isEmpty()) {
|
||||
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
|
||||
return;
|
||||
}
|
||||
if (!resJson.containsKey("list") || resJson.getJsonArray("list").isEmpty()) {
|
||||
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
|
||||
return;
|
||||
}
|
||||
// 文件Id
|
||||
JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0);
|
||||
// 如果是目录返回目录ID
|
||||
if (!fileInfo.containsKey("fileList") || fileInfo.getJsonArray("fileList").isEmpty()) {
|
||||
fail(FIRST_REQUEST_URL + " 文件列表为空: " + fileInfo);
|
||||
return;
|
||||
}
|
||||
JsonObject fileList = fileInfo.getJsonArray("fileList").getJsonObject(0);
|
||||
if (fileList.getInteger("fileType") == 2) {
|
||||
promise.complete(fileList.getInteger("folderId").toString());
|
||||
return;
|
||||
}
|
||||
|
||||
String fileId = fileInfo.getString("fileIds");
|
||||
String userId = fileInfo.getString("userId");
|
||||
// 其他参数
|
||||
// String fidEncode = AESUtils.encrypt2HexIz(fileId + "|");
|
||||
String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId);
|
||||
String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs);
|
||||
// 第二次请求
|
||||
clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.setTemplateParam("fidEncode", fidEncode)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
.setTemplateParam("auth", auth)
|
||||
.setTemplateParam("shareId", shareId)
|
||||
.putHeaders(header).send().onSuccess(res2 -> {
|
||||
MultiMap headers = res2.headers();
|
||||
if (!headers.contains("Location")) {
|
||||
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + headers);
|
||||
return;
|
||||
}
|
||||
promise.complete(headers.get("Location"));
|
||||
}).onFailure(handleFail(SECOND_REQUEST_URL));
|
||||
handleParseResponse(resBody, shareId);
|
||||
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
});
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private void handleParseResponse(String resBody, String shareId) {
|
||||
JsonObject resJson;
|
||||
try {
|
||||
resJson = new JsonObject(resBody);
|
||||
} catch (Exception e) {
|
||||
fail(FIRST_REQUEST_URL + " 解析JSON失败: " + resBody);
|
||||
return;
|
||||
}
|
||||
if (resJson.isEmpty()) {
|
||||
fail(FIRST_REQUEST_URL + " 返回内容为空");
|
||||
return;
|
||||
}
|
||||
if (resJson.getInteger("code") != 200) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
return;
|
||||
}
|
||||
if (resJson.getJsonArray("list").isEmpty()) {
|
||||
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
|
||||
return;
|
||||
}
|
||||
if (!resJson.containsKey("list") || resJson.getJsonArray("list").isEmpty()) {
|
||||
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
|
||||
return;
|
||||
}
|
||||
// 文件Id
|
||||
JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0);
|
||||
// 如果是目录返回目录ID
|
||||
if (!fileInfo.containsKey("fileList") || fileInfo.getJsonArray("fileList").isEmpty()) {
|
||||
fail(FIRST_REQUEST_URL + " 文件列表为空: " + fileInfo);
|
||||
return;
|
||||
}
|
||||
JsonObject fileList = fileInfo.getJsonArray("fileList").getJsonObject(0);
|
||||
if (fileList.getInteger("fileType") == 2) {
|
||||
promise.complete(fileList.getInteger("folderId").toString());
|
||||
return;
|
||||
}
|
||||
|
||||
String fileId = fileInfo.getString("fileIds");
|
||||
String userId = fileInfo.getString("userId");
|
||||
// 其他参数
|
||||
// String fidEncode = AESUtils.encrypt2HexIz(fileId + "|");
|
||||
String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId);
|
||||
String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs);
|
||||
// 第二次请求
|
||||
webClientSession.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.setTemplateParam("fidEncode", fidEncode)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
.setTemplateParam("auth", auth)
|
||||
.setTemplateParam("shareId", shareId)
|
||||
.putHeaders(header).send().onSuccess(res2 -> {
|
||||
MultiMap headers = res2.headers();
|
||||
if (!headers.contains("Location")) {
|
||||
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + headers);
|
||||
return;
|
||||
}
|
||||
promise.complete(headers.get("Location"));
|
||||
}).onFailure(handleFail(SECOND_REQUEST_URL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<List<FileInfo>> parseFileList() {
|
||||
Promise<List<FileInfo>> promise = Promise.promise();
|
||||
@@ -174,7 +221,7 @@ public class IzTool extends PanBase {
|
||||
log.debug("开始解析目录: {}, shareId: {}, uuid: {}, ts: {}", id, shareId, uuid, tsEncode);
|
||||
// 开始解析目录: 164312216, shareId: bPMsbg5K, uuid: 0fmVWTx2Ea4zFwkpd7KXf, ts: 20865d7b7f00828279f437cd1f097860
|
||||
// 拿到目录ID
|
||||
client.postAbs(UriTemplate.of(FILE_LIST_URL))
|
||||
webClientSession.postAbs(UriTemplate.of(FILE_LIST_URL))
|
||||
.putHeaders(header)
|
||||
.setTemplateParam("shareId", shareId)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
@@ -264,7 +311,7 @@ public class IzTool extends PanBase {
|
||||
public Future<String> parseById() {
|
||||
// 第二次请求
|
||||
JsonObject paramJson = (JsonObject)shareLinkInfo.getOtherParam().get("paramJson");
|
||||
clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
webClientSession.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.setTemplateParam("fidEncode", paramJson.getString("fidEncode"))
|
||||
.setTemplateParam("uuid", paramJson.getString("uuid"))
|
||||
.setTemplateParam("ts", paramJson.getString("ts"))
|
||||
|
||||
@@ -1,30 +1,72 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.FileInfo;
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.FileSizeConverter;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* <a href="https://lecloud.lenovo.com/">联想乐云</a>
|
||||
*/
|
||||
public class LeTool extends PanBase {
|
||||
private static final String API_URL_PREFIX = "https://lecloud.lenovo.com/share/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 int FILE_TYPE_DIRECTORY = 0; // 目录类型
|
||||
|
||||
private static final MultiMap HEADERS;
|
||||
|
||||
static {
|
||||
HEADERS = MultiMap.caseInsensitiveMultiMap();
|
||||
HEADERS.set("Accept", "application/json, text/plain, */*");
|
||||
HEADERS.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");
|
||||
HEADERS.set("Cache-Control", "no-cache");
|
||||
HEADERS.set("Connection", "keep-alive");
|
||||
HEADERS.set("Content-Type", "application/json");
|
||||
HEADERS.set("DNT", "1");
|
||||
HEADERS.set("Origin", "https://lecloud.lenovo.com");
|
||||
HEADERS.set("Pragma", "no-cache");
|
||||
HEADERS.set("Sec-Fetch-Dest", "empty");
|
||||
HEADERS.set("Sec-Fetch-Mode", "cors");
|
||||
HEADERS.set("Sec-Fetch-Site", "same-origin");
|
||||
HEADERS.set("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1 Edg/143.0.0.0");
|
||||
}
|
||||
|
||||
public LeTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取干净的 shareId(去掉可能的查询参数)
|
||||
* URL 如 https://lecloud.lenovo.com/share/5eoN3RA5PLhQcH4zE?path=... 会导致 shareKey 包含查询参数
|
||||
*/
|
||||
private String getCleanShareId() {
|
||||
String shareKey = shareLinkInfo.getShareKey();
|
||||
if (shareKey != null && shareKey.contains("?")) {
|
||||
return shareKey.split("\\?")[0];
|
||||
}
|
||||
return shareKey;
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
final String dataKey = getCleanShareId();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
// {"shareId":"xxx","password":"xxx","directoryId":"-1"}
|
||||
String apiUrl1 = API_URL_PREFIX + "shareInfo";
|
||||
client.postAbs(apiUrl1)
|
||||
.sendJsonObject(JsonObject.of("shareId", dataKey, "password", pwd, "directoryId", -1))
|
||||
.putHeaders(HEADERS)
|
||||
.sendJsonObject(JsonObject.of("shareId", dataKey, "password", pwd, "directoryId", "-1"))
|
||||
.onSuccess(res -> {
|
||||
JsonObject resJson = asJson(res);
|
||||
if (resJson.containsKey("result")) {
|
||||
@@ -44,7 +86,19 @@ public class LeTool extends PanBase {
|
||||
}
|
||||
JsonObject fileInfoJson = files.getJsonObject(0);
|
||||
if (fileInfoJson != null) {
|
||||
// TODO 文件大小fileSize和文件名fileName
|
||||
// Extract and populate FileInfo
|
||||
FileInfo fileInfo = createFileInfo(fileInfoJson);
|
||||
shareLinkInfo.getOtherParam().put("fileInfo", fileInfo);
|
||||
|
||||
// 判断是否为目录
|
||||
Integer fileType = fileInfoJson.getInteger("fileType");
|
||||
if (fileType != null && fileType == FILE_TYPE_DIRECTORY) {
|
||||
// 如果是目录,返回目录ID
|
||||
String fileId = fileInfoJson.getString("fileId");
|
||||
promise.complete(fileId);
|
||||
return;
|
||||
}
|
||||
|
||||
String fileId = fileInfoJson.getString("fileId");
|
||||
// 根据文件ID获取跳转链接
|
||||
getDownURL(dataKey, fileId);
|
||||
@@ -59,13 +113,205 @@ public class LeTool extends PanBase {
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<List<FileInfo>> parseFileList() {
|
||||
Promise<List<FileInfo>> listPromise = Promise.promise();
|
||||
|
||||
String dataKey = getCleanShareId();
|
||||
|
||||
// 如果参数里的目录ID不为空,则直接解析目录
|
||||
String dirId = (String) shareLinkInfo.getOtherParam().get("dirId");
|
||||
if (dirId == null || dirId.isEmpty()) {
|
||||
// 如果没有指定目录ID,使用根目录ID "-1"
|
||||
dirId = "-1";
|
||||
}
|
||||
|
||||
// 直接请求shareInfo接口解析目录
|
||||
parseDirectory(dirId, dataKey, listPromise);
|
||||
return listPromise.future();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析目录下的文件列表
|
||||
*/
|
||||
private void parseDirectory(String directoryId, String shareId, Promise<List<FileInfo>> promise) {
|
||||
String pwd = shareLinkInfo.getSharePassword();
|
||||
if (pwd == null) {
|
||||
pwd = "";
|
||||
}
|
||||
String apiUrl = API_URL_PREFIX + "shareInfo";
|
||||
|
||||
JsonObject requestBody = JsonObject.of("shareId", shareId, "password", pwd, "directoryId", directoryId);
|
||||
log.info("解析目录请求: url={}, body={}", apiUrl, requestBody.encode());
|
||||
|
||||
client.postAbs(apiUrl)
|
||||
.putHeaders(HEADERS)
|
||||
.sendJsonObject(requestBody)
|
||||
.onSuccess(res -> {
|
||||
JsonObject resJson = asJson(res);
|
||||
|
||||
if (!resJson.containsKey("result") || !resJson.getBoolean("result")) {
|
||||
promise.fail("解析目录失败: " + resJson.encode());
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject dataJson = resJson.getJsonObject("data");
|
||||
if (!dataJson.getBoolean("passwordVerified")) {
|
||||
promise.fail("密码验证失败");
|
||||
return;
|
||||
}
|
||||
|
||||
JsonArray files = dataJson.getJsonArray("files");
|
||||
if (files == null || files.isEmpty()) {
|
||||
promise.complete(new ArrayList<>());
|
||||
return;
|
||||
}
|
||||
|
||||
List<FileInfo> fileList = new ArrayList<>();
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
JsonObject fileJson = files.getJsonObject(i);
|
||||
FileInfo fileInfo = createFileInfoForList(fileJson, shareId);
|
||||
fileList.add(fileInfo);
|
||||
}
|
||||
|
||||
promise.complete(fileList);
|
||||
})
|
||||
.onFailure(err -> {
|
||||
log.error("解析目录请求失败: {}", err.getMessage());
|
||||
promise.fail(err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 为文件列表创建 FileInfo 对象
|
||||
*/
|
||||
private FileInfo createFileInfoForList(JsonObject fileJson, String shareId) {
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
|
||||
try {
|
||||
String fileId = fileJson.getString("fileId");
|
||||
String fileName = fileJson.getString("fileName");
|
||||
Long fileSize = fileJson.getLong("fileSize");
|
||||
Integer fileType = fileJson.getInteger("fileType");
|
||||
|
||||
fileInfo.setFileId(fileId);
|
||||
fileInfo.setFileName(fileName);
|
||||
fileInfo.setPanType(shareLinkInfo.getType());
|
||||
|
||||
// 判断是否为目录
|
||||
if (fileType != null && fileType == FILE_TYPE_DIRECTORY) {
|
||||
// 目录类型
|
||||
fileInfo.setFileType("folder");
|
||||
fileInfo.setSize(0L);
|
||||
fileInfo.setSizeStr("0B");
|
||||
// 设置目录解析的URL - fileId 需要进行 URL 编码以保持特殊字符的编码状态
|
||||
try {
|
||||
String encodedFileId = URLEncoder.encode(fileId, "UTF-8");
|
||||
fileInfo.setParserUrl(String.format("%s/v2/getFileList?url=%s&dirId=%s",
|
||||
getDomainName(),
|
||||
shareLinkInfo.getShareUrl(),
|
||||
encodedFileId));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.error("URL编码失败: {}", e.getMessage());
|
||||
// 降级方案:直接使用原始 fileId
|
||||
fileInfo.setParserUrl(String.format("%s/v2/getFileList?url=%s&dirId=%s",
|
||||
getDomainName(),
|
||||
shareLinkInfo.getShareUrl(),
|
||||
fileId));
|
||||
}
|
||||
} else {
|
||||
// 文件类型
|
||||
fileInfo.setFileType(fileType != null ? String.valueOf(fileType) : DEFAULT_FILE_TYPE);
|
||||
fileInfo.setSize(fileSize);
|
||||
fileInfo.setSizeStr(FileSizeConverter.convertToReadableSize(fileSize));
|
||||
|
||||
// 创建参数JSON并编码为Base64
|
||||
JsonObject paramJson = JsonObject.of(
|
||||
"shareId", shareId,
|
||||
"fileId", fileId
|
||||
);
|
||||
String paramBase64 = Base64.getEncoder().encodeToString(paramJson.encode().getBytes());
|
||||
|
||||
// 设置解析URL和预览URL
|
||||
fileInfo.setParserUrl(String.format("%s/v2/redirectUrl/%s/%s",
|
||||
getDomainName(),
|
||||
shareLinkInfo.getType(),
|
||||
paramBase64))
|
||||
.setPreviewUrl(String.format("%s/v2/viewUrl/%s/%s",
|
||||
getDomainName(),
|
||||
shareLinkInfo.getType(),
|
||||
paramBase64));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("创建文件信息失败: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<String> parseById() {
|
||||
Promise<String> parsePromise = Promise.promise();
|
||||
|
||||
try {
|
||||
// 从参数中获取解析所需的信息
|
||||
JsonObject paramJson = (JsonObject) shareLinkInfo.getOtherParam().get("paramJson");
|
||||
String shareId = paramJson.getString("shareId");
|
||||
String fileId = paramJson.getString("fileId");
|
||||
|
||||
// 调用获取下载链接
|
||||
getDownURLForById(shareId, fileId, parsePromise);
|
||||
|
||||
} catch (Exception e) {
|
||||
parsePromise.fail("解析参数失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
return parsePromise.future();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件ID获取下载URL (用于 parseById)
|
||||
*/
|
||||
private void getDownURLForById(String shareId, String fileId, Promise<String> promise) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
JsonArray fileIds = JsonArray.of(fileId);
|
||||
String apiUrl = API_URL_PREFIX + "packageDownloadWithFileIds";
|
||||
|
||||
client.postAbs(apiUrl)
|
||||
.putHeaders(HEADERS)
|
||||
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", shareId, "browserId", uuid))
|
||||
.onSuccess(res -> {
|
||||
JsonObject resJson = asJson(res);
|
||||
if (resJson.containsKey("result")) {
|
||||
if (resJson.getBoolean("result")) {
|
||||
JsonObject dataJson = resJson.getJsonObject("data");
|
||||
String downloadUrl = dataJson.getString("downloadUrl");
|
||||
if (downloadUrl == null) {
|
||||
promise.fail("Result JSON数据异常: downloadUrl不存在");
|
||||
return;
|
||||
}
|
||||
// 获取重定向链接
|
||||
clientNoRedirects.getAbs(downloadUrl).send()
|
||||
.onSuccess(res2 -> promise.complete(res2.headers().get("Location")))
|
||||
.onFailure(err -> promise.fail(err));
|
||||
} else {
|
||||
promise.fail(resJson.getString("errcode") + ": " + resJson.getString("errmsg"));
|
||||
}
|
||||
} else {
|
||||
promise.fail("Result JSON数据异常: result字段不存在");
|
||||
}
|
||||
}).onFailure(err -> promise.fail(err));
|
||||
}
|
||||
|
||||
private void getDownURL(String key, String 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))
|
||||
.putHeaders(HEADERS)
|
||||
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", key, "browserId", uuid))
|
||||
.onSuccess(res -> {
|
||||
JsonObject resJson = asJson(res);
|
||||
if (resJson.containsKey("result")) {
|
||||
@@ -89,4 +335,51 @@ public class LeTool extends PanBase {
|
||||
}
|
||||
}).onFailure(handleFail(apiUrl2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create FileInfo object from JSON response
|
||||
* Uses exact field names from the API response without fallback checks
|
||||
*/
|
||||
private FileInfo createFileInfo(JsonObject fileInfoJson) {
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
|
||||
try {
|
||||
// Set fileId
|
||||
String fileId = fileInfoJson.getString("fileId");
|
||||
if (fileId != null) {
|
||||
fileInfo.setFileId(fileId);
|
||||
}
|
||||
|
||||
// Set fileName
|
||||
String fileName = fileInfoJson.getString("fileName");
|
||||
if (fileName != null) {
|
||||
fileInfo.setFileName(fileName);
|
||||
}
|
||||
|
||||
// Set file size
|
||||
Long fileSize = fileInfoJson.getLong("fileSize");
|
||||
if (fileSize != null) {
|
||||
fileInfo.setSize(fileSize);
|
||||
// Convert to readable size string
|
||||
fileInfo.setSizeStr(FileSizeConverter.convertToReadableSize(fileSize));
|
||||
}
|
||||
|
||||
// Set fileType (API returns it as an integer)
|
||||
Integer fileTypeInt = fileInfoJson.getInteger("fileType");
|
||||
if (fileTypeInt != null) {
|
||||
fileInfo.setFileType(String.valueOf(fileTypeInt));
|
||||
} else {
|
||||
// Default to generic file type if not available
|
||||
fileInfo.setFileType(DEFAULT_FILE_TYPE);
|
||||
}
|
||||
|
||||
// Set panType
|
||||
fileInfo.setPanType(shareLinkInfo.getType());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("Error extracting file info from JSON: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return fileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,12 @@ import io.vertx.core.Promise;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import io.vertx.ext.web.client.WebClientSession;
|
||||
import org.apache.commons.lang3.RegExUtils;
|
||||
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -29,13 +27,14 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class LzTool extends PanBase {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://wwwwp.lanzoup.com";
|
||||
WebClientSession webClientSession = WebClientSession.create(clientNoRedirects);
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://w1.lanzn.com/";
|
||||
MultiMap headers0 = HeaderUtils.parseHeaders("""
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
|
||||
Accept-Encoding: gzip, deflate
|
||||
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
|
||||
Cache-Control: max-age=0
|
||||
Cookie: codelen=1; pc_ad1=1
|
||||
DNT: 1
|
||||
Priority: u=0, i
|
||||
Sec-CH-UA: "Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"
|
||||
@@ -63,53 +62,100 @@ public class LzTool extends PanBase {
|
||||
.putHeaders(headers0)
|
||||
.send().onSuccess(res -> {
|
||||
String html = asText(res);
|
||||
try {
|
||||
setFileInfo(html, shareLinkInfo);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 匹配iframe
|
||||
Pattern compile = Pattern.compile("src=\"(/fn\\?[a-zA-Z\\d_+/=]{16,})\"");
|
||||
Matcher matcher = compile.matcher(html);
|
||||
// 没有Iframe说明是加密分享, 匹配sign通过密码请求下载页面
|
||||
if (!matcher.find()) {
|
||||
try {
|
||||
String jsText = getJsByPwd(pwd, html, "document.getElementById('rpt')");
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "down_p");
|
||||
getDownURL(sUrl, client, scriptObjectMirror);
|
||||
} catch (Exception e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
}
|
||||
} else {
|
||||
// 没有密码
|
||||
String iframePath = matcher.group(1);
|
||||
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
|
||||
String html2 = res2.bodyAsString();
|
||||
if (html.contains("var arg1='")) {
|
||||
webClientSession = WebClientSession.create(clientNoRedirects);
|
||||
setCookie(html);
|
||||
webClientSession.getAbs(sUrl)
|
||||
.putHeaders(headers0)
|
||||
.send().onSuccess(res2 -> {
|
||||
String html2 = asText(res2);
|
||||
doParser(html2, pwd, sUrl);
|
||||
});
|
||||
|
||||
// 去TMD正则
|
||||
// Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
|
||||
String jsText = getJsText(html2);
|
||||
if (jsText == null) {
|
||||
fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": js脚本匹配失败, 可能分享已失效");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, null);
|
||||
getDownURL(sUrl, client, scriptObjectMirror);
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
}
|
||||
}).onFailure(handleFail(SHARE_URL_PREFIX));
|
||||
} else {
|
||||
doParser(html, pwd, sUrl);
|
||||
}
|
||||
|
||||
}).onFailure(handleFail(sUrl));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private void doParser(String html, String pwd, String sUrl) {
|
||||
try {
|
||||
setFileInfo(html, shareLinkInfo);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 匹配iframe
|
||||
Pattern compile = Pattern.compile("src=\"(/fn\\?[a-zA-Z\\d_+/=]{16,})\"");
|
||||
Matcher matcher = compile.matcher(html);
|
||||
// 没有Iframe说明是加密分享, 匹配sign通过密码请求下载页面
|
||||
if (!matcher.find()) {
|
||||
try {
|
||||
String jsText = getJsByPwd(pwd, html, "document.getElementById('rpt')");
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "down_p");
|
||||
getDownURL(sUrl, scriptObjectMirror);
|
||||
} catch (Exception e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 没有密码
|
||||
String iframePath = matcher.group(1);
|
||||
String absoluteURI = SHARE_URL_PREFIX + iframePath;
|
||||
webClientSession.getAbs(absoluteURI).putHeaders(headers0).send().onSuccess(res2 -> {
|
||||
String html2= asText(res2);
|
||||
// Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
|
||||
String jsText = getJsText(html2);
|
||||
if (jsText == null) {
|
||||
headers0.add("Referer", absoluteURI);
|
||||
setCookie(html2);
|
||||
webClientSession.getAbs(absoluteURI).send().onSuccess(res3 -> {
|
||||
String html3= asText(res3);
|
||||
String jsText3 = getJsText(html3);
|
||||
if (jsText3 != null) {
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText3, null);
|
||||
getDownURL(sUrl, scriptObjectMirror);
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "引擎执行失败");
|
||||
}
|
||||
} else {
|
||||
|
||||
fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": 获取失败0, 可能分享已失效");
|
||||
return;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, null);
|
||||
getDownURL(sUrl, scriptObjectMirror);
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
}
|
||||
}
|
||||
}).onFailure(handleFail(SHARE_URL_PREFIX));
|
||||
}
|
||||
}
|
||||
|
||||
private void setCookie(String html2) {
|
||||
int beginIndex = html2.indexOf("arg1='") + 6;
|
||||
String arg1 = html2.substring(beginIndex, html2.indexOf("';", beginIndex));
|
||||
String acw_sc__v2 = AcwScV2Generator.acwScV2Simple(arg1);
|
||||
// 创建一个 Cookie 并放入 CookieStore
|
||||
DefaultCookie nettyCookie = new DefaultCookie("acw_sc__v2", acw_sc__v2);
|
||||
nettyCookie.setDomain(".lanzn.com"); // 设置域名
|
||||
nettyCookie.setPath("/"); // 设置路径
|
||||
nettyCookie.setSecure(false);
|
||||
nettyCookie.setHttpOnly(false);
|
||||
webClientSession.cookieStore().put(nettyCookie);
|
||||
}
|
||||
|
||||
private String getJsByPwd(String pwd, String html, String subText) {
|
||||
String jsText = getJsText(html);
|
||||
|
||||
if (jsText == null) {
|
||||
throw new RuntimeException("js脚本匹配失败, 可能分享已失效");
|
||||
throw new RuntimeException("获取失败1, 可能分享已失效");
|
||||
}
|
||||
jsText = jsText.replace("document.getElementById('pwd').value", "\"" + pwd + "\"");
|
||||
int i = jsText.indexOf(subText);
|
||||
@@ -131,7 +177,7 @@ public class LzTool extends PanBase {
|
||||
return html.substring(startPos, endPos).replaceAll("<!--.*-->", "");
|
||||
}
|
||||
|
||||
private void getDownURL(String key, WebClient client, Map<String, ?> obj) {
|
||||
private void getDownURL(String key, Map<String, ?> obj) {
|
||||
if (obj == null) {
|
||||
fail("需要访问密码");
|
||||
return;
|
||||
@@ -163,7 +209,7 @@ public class LzTool extends PanBase {
|
||||
headers.set("referer", key);
|
||||
// action=downprocess&signs=%3Fctdf&websignkey=I5gl&sign=BWMGOF1sBTRWXwI9BjZdYVA7BDhfNAIyUG9UawJtUGMIPlAhACkCa1UyUTAAYFxvUj5XY1E7UGFXaFVq&websign=&kd=1&ves=1
|
||||
String url = SHARE_URL_PREFIX + url0;
|
||||
client.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
|
||||
webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
|
||||
try {
|
||||
JsonObject urlJson = asJson(res2);
|
||||
String name = urlJson.getString("inf");
|
||||
@@ -178,7 +224,6 @@ public class LzTool extends PanBase {
|
||||
|
||||
String downUrl = urlJson.getString("dom") + "/file/" + urlJson.getString("url");
|
||||
headers.remove("Referer");
|
||||
WebClientSession webClientSession = WebClientSession.create(client);
|
||||
webClientSession.getAbs(downUrl).putHeaders(headers).send()
|
||||
.onSuccess(res3 -> {
|
||||
String location = res3.headers().get("Location");
|
||||
@@ -195,12 +240,13 @@ public class LzTool extends PanBase {
|
||||
nettyCookie.setPath("/"); // 设置路径
|
||||
nettyCookie.setSecure(false);
|
||||
nettyCookie.setHttpOnly(false);
|
||||
webClientSession.cookieStore().put(nettyCookie);
|
||||
webClientSession.getAbs(downUrl).putHeaders(headers).send()
|
||||
WebClientSession webClientSession2 = WebClientSession.create(clientNoRedirects);
|
||||
webClientSession2.cookieStore().put(nettyCookie);
|
||||
webClientSession2.getAbs(downUrl).putHeaders(headers).send()
|
||||
.onSuccess(res4 -> {
|
||||
String location0 = res4.headers().get("Location");
|
||||
if (location0 == null) {
|
||||
fail(downUrl + " -> 直链获取失败, 可能分享已失效");
|
||||
fail(downUrl + " -> 直链获取失败2, 可能分享已失效");
|
||||
} else {
|
||||
setDateAndComplate(location0);
|
||||
}
|
||||
@@ -248,67 +294,98 @@ public class LzTool extends PanBase {
|
||||
String sUrl = shareLinkInfo.getShareUrl();
|
||||
String pwd = shareLinkInfo.getSharePassword();
|
||||
|
||||
WebClient client = clientNoRedirects;
|
||||
client.getAbs(sUrl).send().onSuccess(res -> {
|
||||
webClientSession.getAbs(sUrl).send().onSuccess(res -> {
|
||||
String html = res.bodyAsString();
|
||||
try {
|
||||
String jsText = getJsByPwd(pwd, html, "var urls =window.location.href");
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "file");
|
||||
Map<String, Object> data = CastUtil.cast(scriptObjectMirror.get("data"));
|
||||
MultiMap map = MultiMap.caseInsensitiveMultiMap();
|
||||
data.forEach((k, v) -> map.set(k, v.toString()));
|
||||
log.debug("解析参数: {}", map);
|
||||
MultiMap headers = getHeaders(sUrl);
|
||||
|
||||
String url = SHARE_URL_PREFIX + "/filemoreajax.php?file=" + data.get("fid");
|
||||
client.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
|
||||
JsonObject fileListJson = asJson(res2);
|
||||
if (fileListJson.getInteger("zt") != 1) {
|
||||
promise.fail(baseMsg() + fileListJson.getString("info"));
|
||||
return;
|
||||
}
|
||||
List<FileInfo> list = new ArrayList<>();
|
||||
fileListJson.getJsonArray("text").forEach(item -> {
|
||||
/*
|
||||
{
|
||||
"icon": "apk",
|
||||
"t": 0,
|
||||
"id": "iULV2n4361c",
|
||||
"name_all": "xx.apk",
|
||||
"size": "49.8 M",
|
||||
"time": "2021-03-19",
|
||||
"duan": "in4361",
|
||||
"p_ico": 0
|
||||
}
|
||||
*/
|
||||
JsonObject fileJson = (JsonObject) item;
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
String size = fileJson.getString("size");
|
||||
Long sizeNum = FileSizeConverter.convertToBytes(size);
|
||||
String panType = shareLinkInfo.getType();
|
||||
String id = fileJson.getString("id");
|
||||
fileInfo.setFileName(fileJson.getString("name_all"))
|
||||
.setFileId(id)
|
||||
.setCreateTime(fileJson.getString("time"))
|
||||
.setFileType(fileJson.getString("icon"))
|
||||
.setSizeStr(fileJson.getString("size"))
|
||||
.setSize(sizeNum)
|
||||
.setPanType(panType)
|
||||
.setParserUrl(getDomainName() + "/d/" + panType + "/" + id)
|
||||
.setPreviewUrl(String.format("%s/v2/view/%s/%s", getDomainName(),
|
||||
shareLinkInfo.getType(), id));
|
||||
log.debug("文件信息: {}", fileInfo);
|
||||
list.add(fileInfo);
|
||||
});
|
||||
promise.complete(list);
|
||||
});
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
promise.fail(e);
|
||||
// 检查是否需要 cookie 验证
|
||||
if (html.contains("var arg1='")) {
|
||||
webClientSession = WebClientSession.create(clientNoRedirects);
|
||||
setCookie(html);
|
||||
// 重新请求
|
||||
webClientSession.getAbs(sUrl).send().onSuccess(res2 -> {
|
||||
handleFileListParse(res2.bodyAsString(), pwd, sUrl, promise);
|
||||
}).onFailure(err -> promise.fail(err));
|
||||
return;
|
||||
}
|
||||
});
|
||||
handleFileListParse(html, pwd, sUrl, promise);
|
||||
}).onFailure(err -> promise.fail(err));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private void handleFileListParse(String html, String pwd, String sUrl, Promise<List<FileInfo>> promise) {
|
||||
try {
|
||||
String jsText = getJsByPwd(pwd, html, "var urls =window.location.href");
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "file");
|
||||
Map<String, Object> data = CastUtil.cast(scriptObjectMirror.get("data"));
|
||||
MultiMap map = MultiMap.caseInsensitiveMultiMap();
|
||||
data.forEach((k, v) -> map.set(k, v.toString()));
|
||||
log.debug("解析参数: {}", map);
|
||||
MultiMap headers = getHeaders(sUrl);
|
||||
|
||||
String url = SHARE_URL_PREFIX + "/filemoreajax.php?file=" + data.get("fid");
|
||||
webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
|
||||
String resBody = asText(res2);
|
||||
// 再次检查是否需要 cookie 验证
|
||||
if (resBody.contains("var arg1='")) {
|
||||
setCookie(resBody);
|
||||
// 重新请求
|
||||
webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res3 -> {
|
||||
handleFileListResponse(asText(res3), promise);
|
||||
}).onFailure(err -> promise.fail(err));
|
||||
return;
|
||||
}
|
||||
handleFileListResponse(resBody, promise);
|
||||
}).onFailure(err -> promise.fail(err));
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
promise.fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFileListResponse(String responseBody, Promise<List<FileInfo>> promise) {
|
||||
try {
|
||||
JsonObject fileListJson = new JsonObject(responseBody);
|
||||
if (fileListJson.getInteger("zt") != 1) {
|
||||
promise.fail(baseMsg() + fileListJson.getString("info"));
|
||||
return;
|
||||
}
|
||||
List<FileInfo> list = new ArrayList<>();
|
||||
fileListJson.getJsonArray("text").forEach(item -> {
|
||||
/*
|
||||
{
|
||||
"icon": "apk",
|
||||
"t": 0,
|
||||
"id": "iULV2n4361c",
|
||||
"name_all": "xx.apk",
|
||||
"size": "49.8 M",
|
||||
"time": "2021-03-19",
|
||||
"duan": "in4361",
|
||||
"p_ico": 0
|
||||
}
|
||||
*/
|
||||
JsonObject fileJson = (JsonObject) item;
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
String size = fileJson.getString("size");
|
||||
Long sizeNum = FileSizeConverter.convertToBytes(size);
|
||||
String panType = shareLinkInfo.getType();
|
||||
String id = fileJson.getString("id");
|
||||
fileInfo.setFileName(fileJson.getString("name_all"))
|
||||
.setFileId(id)
|
||||
.setCreateTime(fileJson.getString("time"))
|
||||
.setFileType(fileJson.getString("icon"))
|
||||
.setSizeStr(fileJson.getString("size"))
|
||||
.setSize(sizeNum)
|
||||
.setPanType(panType)
|
||||
.setParserUrl(getDomainName() + "/d/" + panType + "/" + id)
|
||||
.setPreviewUrl(String.format("%s/v2/view/%s/%s", getDomainName(),
|
||||
shareLinkInfo.getType(), id));
|
||||
log.debug("文件信息: {}", fileInfo);
|
||||
list.add(fileInfo);
|
||||
});
|
||||
promise.complete(list);
|
||||
} catch (Exception e) {
|
||||
promise.fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
void setFileInfo(String html, ShareLinkInfo shareLinkInfo) {
|
||||
// 写入 fileInfo
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
|
||||
@@ -400,7 +400,7 @@ export default {
|
||||
const data = result.data
|
||||
|
||||
// 检查是否支持目录解析
|
||||
const supportedPans = ["iz", "lz", "fj", "ye"]
|
||||
const supportedPans = ["iz", "lz", "fj", "ye", "le"]
|
||||
if (!supportedPans.includes(data.shareLinkInfo.type)) {
|
||||
this.$message.error("当前网盘不支持目录解析")
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user