Compare commits

...

10 Commits

Author SHA1 Message Date
qaiu
f6209a8959 更新 README.md
添加夸克,uc解析
2026-02-03 16:18:55 +08:00
q
72ed0ea8f8 Fixed 蓝奏云目录解析cookie验证问题 2026-02-03 13:27:09 +08:00
qaiu
d698f82299 Update Playground password protection link in README 2026-02-03 10:41:42 +08:00
q
ba4666c32a Fixed 蓝奏优享解析,v019b21 2026-02-02 16:02:36 +08:00
q
d6d37e8204 fixed: 修复蓝奏优享 #159, #158 2026-02-02 15:59:44 +08:00
q
cb9dbfcc69 蓝奏云规则更新 2026-02-01 10:33:25 +08:00
qaiu
42b366ed0f Clean up launch configurations in launch.json,run AppMain
Removed unused Java launch configurations from launch.json.
2026-01-27 07:53:20 +08:00
qaiu
4021c507b6 更新 README.md, 添加接口文档https://nfdparser.apifox.cn 2026-01-27 00:05:56 +08:00
qaiu
4d8e82080d 189.qaiu.top 大文件解析体验版,支持天翼云盘,移动云盘等
#153 #156 #119 #92
2026-01-26 13:47:37 +08:00
qaiu
fca608b44e 更新 README.md
天翼云盘演示站限时体验
2026-01-26 13:09:49 +08:00
5 changed files with 288 additions and 212 deletions

54
.vscode/launch.json vendored
View File

@@ -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"
}
]
}
}

View File

@@ -14,7 +14,7 @@
# netdisk-fast-download 网盘分享链接云解析服务
QQ群1017480890
QQ交流1017480890
netdisk-fast-download网盘直链云解析(nfd云解析)能把网盘分享下载链接转化为直链,支持多款云盘,已支持蓝奏云/蓝奏云优享/奶牛快传/移动云云空间/小飞机盘/亿方云/123云盘/Cloudreve等支持加密分享以及部分网盘文件夹分享。
@@ -40,12 +40,12 @@ https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.
**JavaScript解析器文档** [JavaScript解析器开发指南](parser/doc/JAVASCRIPT_PARSER_GUIDE.md) | [自定义解析器扩展指南](parser/doc/CUSTOM_PARSER_GUIDE.md) | [快速开始](parser/doc/CUSTOM_PARSER_QUICKSTART.md)
**Playground功能** [JS解析器演练场密码保护说明](PLAYGROUND_PASSWORD_PROTECTION.md)
**Playground功能** [JS解析器演练场密码保护说明](web-service/doc/PLAYGROUND_PASSWORD_PROTECTION.md)
## 预览地址
[预览地址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返回数据格式示例**
@@ -88,12 +88,16 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
- Onedrive-pod
- Dropbox-pdp
- iCloud-pic
### 专属版提供
### 专属版提供
- [夸克云盘-qk](https://pan.quark.cn/)
- [UC云盘-uc](https://fast.uc.cn/)
- [移动云盘-p139](https://yun.139.com/)
- [联通云盘-pwo](https://pan.wo.cn/)
- [天翼云盘-p189](https://cloud.189.cn/)
## API接口
[api接口文档](https://nfdparser.apifox.cn/)
### 服务端口
- **6400**: API 服务端口(建议使用 Nginx 代理)

View File

@@ -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/

View File

@@ -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"))

View File

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