Compare commits

...

19 Commits

Author SHA1 Message Date
qaiu
a420bad305 Update README.md 2026-04-09 16:46:29 +08:00
qaiu
6ef6e47580 Update README.md 2026-04-07 08:39:41 +08:00
qaiu
94f83ec296 Fix duplicate Trendshift badge in README
Removed duplicate Trendshift badge from README.
2026-04-07 08:23:44 +08:00
qaiu
702569c701 Add Trendshift badge to README
Added Trendshift badge to README for repository tracking.
2026-04-07 08:22:40 +08:00
qaiu
d4940ca9ee fixed: 123-YePan: Fix regex pattern for share key extraction 2026-04-07 08:20:06 +08:00
qaiu
dbd1c138ca Merge pull request #173 from qaiu/copilot/identify-yifang-cloud-new-format
feat: recognize new Fangcloud /share/ URL format
2026-04-05 17:59:54 +08:00
copilot-swe-agent[bot]
0b49c55cf3 feat: recognize new Fangcloud /share/ URL format in addition to /sharing/ and /s/
Agent-Logs-Url: https://github.com/qaiu/netdisk-fast-download/sessions/dc483348-3899-4448-80ce-c2352e6bc23e

Co-authored-by: qaiu <29825328+qaiu@users.noreply.github.com>
2026-04-05 08:20:46 +00:00
copilot-swe-agent[bot]
b1ec3b2eea Initial plan 2026-04-05 08:16:46 +00:00
qaiu
9ea89feee7 更新 README.md 2026-04-04 20:16:58 +08:00
qaiu
4a843194a3 Merge pull request #171 from qaiu/copilot/update-ws-domain-recognition
feat(WS): 扩展文叔叔域名匹配 + 补充单元测试
2026-03-18 12:21:31 +08:00
copilot-swe-agent[bot]
03503115fd feat: 文叔叔(WS)域名扩展 + 单元测试补充
Co-authored-by: qaiu <29825328+qaiu@users.noreply.github.com>
2026-03-18 02:18:53 +00:00
copilot-swe-agent[bot]
1870aef60e Initial plan 2026-03-18 02:11:33 +00:00
qaiu
ed8fd66d1e 更新 README.md 2026-03-16 20:15:36 +08:00
qaiu
c1c4c8cdc5 更新 README.md 2026-03-16 20:14:57 +08:00
q
256ec3b152 Fixed: Lz parser return filename error. 2026-03-07 13:45:26 +08:00
q
da490e5bbd Merge branch 'main' of github.com:qaiu/netdisk-fast-download 2026-03-06 10:39:49 +08:00
q
ba0ac86eea LzToooool 2026-03-06 10:38:11 +08:00
qaiu
b5544c4131 更新 README.md 2026-02-28 21:31:20 +08:00
qaiu
d94ea6aaf3 更新 README.md 2026-02-25 01:42:46 +08:00
7 changed files with 240 additions and 87 deletions

View File

@@ -1,5 +1,17 @@
<div align="center" style="display:flex; justify-content:center; gap:10px; align-items:flex-start;">
<img
src="https://github.com/user-attachments/assets/bf266d0a-aaf8-4772-9231-e38a4b7bb6cb"
alt="image1"
style="width:300px; max-width:300px; flex:none;"
>
<img
src="https://github.com/user-attachments/assets/bb7a85f0-c256-4b4a-a11b-3ceb55afc302"
alt="image2"
style="width:300px; max-width:300px; flex:none;"
>
</div>
<p align="center"> <p align="center">
<img src="https://github.com/user-attachments/assets/87401aae-b0b6-4ffb-bbeb-44756404d26f" alt="项目预览图" /> <a href="https://trendshift.io/repositories/12101" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12101" alt="qaiu%2Fnetdisk-fast-download | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p> </p>
<p align="center"> <p align="center">
@@ -8,10 +20,6 @@
<a href="https://vertx-china.github.io"><img src="https://img.shields.io/badge/vert.x-4.5.22-blue?style=flat"></a> <a href="https://vertx-china.github.io"><img src="https://img.shields.io/badge/vert.x-4.5.22-blue?style=flat"></a>
<a href="https://raw.githubusercontent.com/qaiu/netdisk-fast-download/master/LICENSE"><img src="https://img.shields.io/github/license/qaiu/netdisk-fast-download?style=flat"></a> <a href="https://raw.githubusercontent.com/qaiu/netdisk-fast-download/master/LICENSE"><img src="https://img.shields.io/github/license/qaiu/netdisk-fast-download?style=flat"></a>
<a href="https://github.com/qaiu/netdisk-fast-download/releases/"><img src="https://img.shields.io/github/v/release/qaiu/netdisk-fast-download?style=flat"></a> <a href="https://github.com/qaiu/netdisk-fast-download/releases/"><img src="https://img.shields.io/github/v/release/qaiu/netdisk-fast-download?style=flat"></a>
</p>
# netdisk-fast-download 网盘分享链接云解析服务 # netdisk-fast-download 网盘分享链接云解析服务
QQ交流群1017480890 QQ交流群1017480890
@@ -21,18 +29,18 @@ netdisk-fast-download网盘直链云解析(nfd云解析)能把网盘分享下载
## 快速开始 ## 快速开始
命令行下载分享文件: 命令行下载分享文件:
```shell ```shell
curl -LOJ "https://lz.qaiu.top/parser?url=https://share.feijipan.com/s/nQOaNRPW&pwd=1234" curl -LOJ "https://lz.qaiu.top/parser?url=https://share.feijipan.com/s/Tk1F2kGQ&pwd=1234"
``` ```
或者使用wget: 或者使用wget:
```shell ```shell
wget -O bilibili.mp4 "https://lz.qaiu.top/parser?url=https://share.feijipan.com/s/nQOaNRPW&pwd=1234" wget -O bilibili.mp4 "https://lz.qaiu.top/parser?url=https://share.feijipan.com/s/Tk1F2kGQ&pwd=1234"
``` ```
或者使用浏览器[直接访问](https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.top%2Fparser%3Furl%3Dhttps%3A%2F%2Fshare.feijipan.com%2Fs%2FnQOaNRPW&name=bilibili.mp4&ext=mp4): 或者使用浏览器[直接访问](https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.top%2Fparser%3Furl%3Dhttps%3A%2F%2Fshare.feijipan.com%2Fs%2FTk1F2kGQ&name=bilibili.mp4&ext=mp4):
``` ```
### 调用演示站下载: ### 调用演示站下载:
https://lz.qaiu.top/parser?url=https://share.feijipan.com/s/nQOaNRPW&pwd=1234 https://lz.qaiu.top/parser?url=https://share.feijipan.com/s/Tk1F2kGQ&pwd=1234
### 调用演示站预览: ### 调用演示站预览:
https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.top%2Fparser%3Furl%3Dhttps%3A%2F%2Fshare.feijipan.com%2Fs%2FnQOaNRPW&name=bilibili.mp4&ext=mp4 https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.top%2Fparser%3Furl%3Dhttps%3A%2F%2Fshare.feijipan.com%2Fs%2FTk1F2kGQ&name=bilibili.mp4&ext=mp4
``` ```
@@ -42,10 +50,10 @@ https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.
**Playground功能** [JS解析器演练场密码保护说明](web-service/doc/PLAYGROUND_PASSWORD_PROTECTION.md) **Playground功能** [JS解析器演练场密码保护说明](web-service/doc/PLAYGROUND_PASSWORD_PROTECTION.md)
## 预览地址 ## 体验地址
[预览地址1](https://lz.qaiu.top) [公益解析1](https://lz.qaiu.top)
[预览地址2](https://lz0.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) main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/netdisk-fast-download/tree/main-jdk11)
**0.1.8及以上版本json接口格式有调整 参考json返回数据格式示例** **0.1.8及以上版本json接口格式有调整 参考json返回数据格式示例**
@@ -61,7 +69,6 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
- [蓝奏云-lz](https://pc.woozooo.com/) - [蓝奏云-lz](https://pc.woozooo.com/)
- [蓝奏云优享-iz](https://www.ilanzou.com/) - [蓝奏云优享-iz](https://www.ilanzou.com/)
- [奶牛快传-cow](https://cowtransfer.com/)
- [移动云云空间-ec](https://www.ecpan.cn/web) - [移动云云空间-ec](https://www.ecpan.cn/web)
- [小飞机网盘-fj](https://www.feijipan.com/) - [小飞机网盘-fj](https://www.feijipan.com/)
- [亿方云-fc](https://www.fangcloud.com/) - [亿方云-fc](https://www.fangcloud.com/)

View File

@@ -12,7 +12,7 @@
<groupId>cn.qaiu</groupId> <groupId>cn.qaiu</groupId>
<artifactId>parser</artifactId> <artifactId>parser</artifactId>
<version>10.2.3</version> <version>10.2.5</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>cn.qaiu:parser</name> <name>cn.qaiu:parser</name>

View File

@@ -95,7 +95,6 @@ public enum PanDomainTemplate {
"lanzv|" + "lanzv|" +
"dmpdmp|" + "dmpdmp|" +
"lanrar|" + "lanrar|" +
"webgetstore|" +
"lanzb|" + "lanzb|" +
"lanzoux|" + "lanzoux|" +
"lanzout|" + "lanzout|" +
@@ -103,7 +102,7 @@ public enum PanDomainTemplate {
"lanzoui|" + "lanzoui|" +
"lanzoug|" + "lanzoug|" +
"lanzoum" + "lanzoum" +
")\\.com/(.+/)?(?<KEY>.+)"), ")\\.com/(?<KEY>.+)"),
"https://w1.lanzn.com/{shareKey}", "https://w1.lanzn.com/{shareKey}",
LzTool.class), LzTool.class),
@@ -122,7 +121,7 @@ public enum PanDomainTemplate {
// https://v2.fangcloud.com/s/ // https://v2.fangcloud.com/s/
FC("亿方云", FC("亿方云",
compile("https://v2\\.fangcloud\\.(com|cn)/(s|sharing)/(?<KEY>.+)"), compile("https://v2\\.fangcloud\\.(com|cn)/(s|share|sharing)/(?<KEY>.+)"),
"https://v2.fangcloud.com/s/{shareKey}", "https://v2.fangcloud.com/s/{shareKey}",
"https://www.fangcloud.com/", "https://www.fangcloud.com/",
FcTool.class), FcTool.class),
@@ -143,9 +142,41 @@ public enum PanDomainTemplate {
compile("https://qfile\\.qq\\.com/q/(?<KEY>.+)"), compile("https://qfile\\.qq\\.com/q/(?<KEY>.+)"),
"https://qfile.qq.com/q/{shareKey}", "https://qfile.qq.com/q/{shareKey}",
QQscTool.class), QQscTool.class),
// https://f.ws59.cn/f/或者https://www.wenshushu.cn/f/ // https://f.ws59.cn/f/ 或者 https://www.wenshushu.cn/f/ 等多个镜像域名
/*
f.wsNN.cn (如 f.ws59.cn, f.ws28.cn 等)
www.wenshushu.cn
新增域名:
www.wenxiaozhan.net
www.wenxiaozhan.cn
www.wss.show
www.ws28.cn
www.wss.email
www.wss1.cn
www.ws59.cn
www.wss.cc
www.wss.pet
www.wss.ink
www.wenxiaozhan.com
www.wenshushu.com
www.wss.zone
*/
WS("文叔叔", WS("文叔叔",
compile("https://(f\\.ws(\\d{2})\\.cn|www\\.wenshushu\\.cn)/f/(?<KEY>.+)"), compile("https://(f\\.ws(\\d{2})\\.cn|" +
"www\\.wenxiaozhan\\.net|" +
"www\\.wenxiaozhan\\.cn|" +
"www\\.wss\\.show|" +
"www\\.ws28\\.cn|" +
"www\\.wss\\.email|" +
"www\\.wss1\\.cn|" +
"www\\.ws59\\.cn|" +
"www\\.wss\\.cc|" +
"www\\.wss\\.pet|" +
"www\\.wss\\.ink|" +
"www\\.wenxiaozhan\\.com|" +
"www\\.wenshushu\\.com|" +
"www\\.wss\\.zone|" +
"www\\.wenshushu\\.cn)/f/(?<KEY>.+)"),
"https://www.wenshushu.cn/f/{shareKey}", "https://www.wenshushu.cn/f/{shareKey}",
WsTool.class), WsTool.class),
// https://www.123pan.com/s/ // https://www.123pan.com/s/
@@ -199,7 +230,7 @@ public enum PanDomainTemplate {
"123635\\.com|" + "123635\\.com|" +
"123242\\.com|" + "123242\\.com|" +
"123795\\.com" + "123795\\.com" +
")/s/(?<KEY>.+)(.html)?"), ")/s/(?<KEY>[a-zA-Z0-9_-]+)(?:\\.html)?"),
"https://www.123pan.com/s/{shareKey}", "https://www.123pan.com/s/{shareKey}",
Ye2Tool.class), Ye2Tool.class),
// https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={code}&isShare=1 // https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={code}&isShare=1

View File

@@ -64,7 +64,7 @@ public class LzTool extends PanBase {
String html = asText(res); String html = asText(res);
if (html.contains("var arg1='")) { if (html.contains("var arg1='")) {
webClientSession = WebClientSession.create(clientNoRedirects); webClientSession = WebClientSession.create(clientNoRedirects);
setCookie(html); setCookie(html, sUrl);
webClientSession.getAbs(sUrl) webClientSession.getAbs(sUrl)
.putHeaders(headers0) .putHeaders(headers0)
.send().onSuccess(res2 -> { .send().onSuccess(res2 -> {
@@ -81,6 +81,29 @@ public class LzTool extends PanBase {
} }
private void doParser(String html, String pwd, String sUrl) { private void doParser(String html, String pwd, String sUrl) {
// 检测是否为目录分享链接 (含 /s/、/b/ 路径段或 b0 开头的路径段)
if (sUrl.matches(".*/(s|b)/[^/]+.*") || sUrl.matches(".*/b0[^/]+.*")) {
fail("该链接为蓝奏云目录分享,请使用目录解析接口");
return;
}
// 若仍是校验页 (parse()中cookie域名与实际URL不匹配时会出现), 重试一次
if (html.contains("var arg1='")) {
webClientSession = WebClientSession.create(clientNoRedirects);
setCookie(html, sUrl);
webClientSession.getAbs(sUrl).putHeaders(headers0).send().onSuccess(res -> {
String html2 = asText(res);
if (html2.contains("var arg1='")) {
fail("蓝奏云反爬校验失败,请稍后重试");
return;
}
doParserInternal(html2, pwd, sUrl);
}).onFailure(handleFail(sUrl));
return;
}
doParserInternal(html, pwd, sUrl);
}
private void doParserInternal(String html, String pwd, String sUrl) {
try { try {
setFileInfo(html, shareLinkInfo); setFileInfo(html, shareLinkInfo);
} catch (Exception e) { } catch (Exception e) {
@@ -98,18 +121,16 @@ public class LzTool extends PanBase {
} catch (Exception e) { } catch (Exception e) {
fail(e, "js引擎执行失败"); fail(e, "js引擎执行失败");
} }
} } else {
else {
// 没有密码 // 没有密码
String iframePath = matcher.group(1); String iframePath = matcher.group(1);
String absoluteURI = SHARE_URL_PREFIX + iframePath; String absoluteURI = SHARE_URL_PREFIX + iframePath;
webClientSession.getAbs(absoluteURI).putHeaders(headers0).send().onSuccess(res2 -> { webClientSession.getAbs(absoluteURI).putHeaders(headers0).send().onSuccess(res2 -> {
String html2 = asText(res2); String html2 = asText(res2);
// Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
String jsText = getJsText(html2); String jsText = getJsText(html2);
if (jsText == null) { if (jsText == null) {
headers0.add("Referer", absoluteURI); headers0.add("Referer", absoluteURI);
setCookie(html2); setCookie(html2, absoluteURI);
webClientSession.getAbs(absoluteURI).send().onSuccess(res3 -> { webClientSession.getAbs(absoluteURI).send().onSuccess(res3 -> {
String html3 = asText(res3); String html3 = asText(res3);
String jsText3 = getJsText(html3); String jsText3 = getJsText(html3);
@@ -121,9 +142,7 @@ public class LzTool extends PanBase {
fail(e, "引擎执行失败"); fail(e, "引擎执行失败");
} }
} else { } else {
fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": 获取失败0, 可能分享已失效"); fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": 获取失败0, 可能分享已失效");
return;
} }
}); });
} else { } else {
@@ -138,14 +157,29 @@ public class LzTool extends PanBase {
} }
} }
private void setCookie(String html2) { private void setCookie(String html, String url) {
int beginIndex = html2.indexOf("arg1='") + 6; int beginIndex = html.indexOf("arg1='") + 6;
String arg1 = html2.substring(beginIndex, html2.indexOf("';", beginIndex)); int endIndex = html.indexOf("';", beginIndex);
if (beginIndex < 6 || endIndex == -1 || endIndex <= beginIndex) {
fail("蓝奏云反爬 arg1 Cookie 解析失败,页面内容异常");
return;
}
String arg1 = html.substring(beginIndex, endIndex);
String acw_sc__v2 = AcwScV2Generator.acwScV2Simple(arg1); String acw_sc__v2 = AcwScV2Generator.acwScV2Simple(arg1);
// 从 URL 中动态提取域名(如 lanzoum.com, lanzoux.com 等)
String domain = ".lanzn.com"; // 默认兜底
try {
java.net.URL urlObj = new java.net.URL(url);
String host = urlObj.getHost(); // e.g. "dzvip.lanzoum.com"
int firstDot = host.indexOf('.');
if (firstDot >= 0) {
domain = host.substring(firstDot); // e.g. ".lanzoum.com"
}
} catch (Exception ignored) {}
// 创建一个 Cookie 并放入 CookieStore // 创建一个 Cookie 并放入 CookieStore
DefaultCookie nettyCookie = new DefaultCookie("acw_sc__v2", acw_sc__v2); DefaultCookie nettyCookie = new DefaultCookie("acw_sc__v2", acw_sc__v2);
nettyCookie.setDomain(".lanzn.com"); // 设置域名 nettyCookie.setDomain(domain);
nettyCookie.setPath("/"); // 设置路径 nettyCookie.setPath("/");
nettyCookie.setSecure(false); nettyCookie.setSecure(false);
nettyCookie.setHttpOnly(false); nettyCookie.setHttpOnly(false);
webClientSession.cookieStore().put(nettyCookie); webClientSession.cookieStore().put(nettyCookie);
@@ -218,7 +252,7 @@ public class LzTool extends PanBase {
return; return;
} }
// 文件名 // 文件名
if (urlJson.containsKey("inf") && urlJson.getMap().get("inf") instanceof Character) { if (urlJson.containsKey("inf") && urlJson.getMap().get("inf") instanceof CharSequence) {
((FileInfo)shareLinkInfo.getOtherParam().get("fileInfo")).setFileName(name); ((FileInfo)shareLinkInfo.getOtherParam().get("fileInfo")).setFileName(name);
} }
@@ -234,10 +268,18 @@ public class LzTool extends PanBase {
int beginIndex = text.indexOf("arg1='") + 6; int beginIndex = text.indexOf("arg1='") + 6;
String arg1 = text.substring(beginIndex, text.indexOf("';", beginIndex)); String arg1 = text.substring(beginIndex, text.indexOf("';", beginIndex));
String acw_sc__v2 = AcwScV2Generator.acwScV2Simple(arg1); String acw_sc__v2 = AcwScV2Generator.acwScV2Simple(arg1);
// 从 downUrl 中动态提取域名
String downDomain = ".lanrar.com";
try {
java.net.URL du = new java.net.URL(downUrl);
String h = du.getHost();
int dot = h.indexOf('.');
if (dot >= 0) downDomain = h.substring(dot);
} catch (Exception ignored) {}
// 创建一个 Cookie 并放入 CookieStore // 创建一个 Cookie 并放入 CookieStore
DefaultCookie nettyCookie = new DefaultCookie("acw_sc__v2", acw_sc__v2); DefaultCookie nettyCookie = new DefaultCookie("acw_sc__v2", acw_sc__v2);
nettyCookie.setDomain(".lanrar.com"); // 设置域名 nettyCookie.setDomain(downDomain);
nettyCookie.setPath("/"); // 设置路径 nettyCookie.setPath("/");
nettyCookie.setSecure(false); nettyCookie.setSecure(false);
nettyCookie.setHttpOnly(false); nettyCookie.setHttpOnly(false);
WebClientSession webClientSession2 = WebClientSession.create(clientNoRedirects); WebClientSession webClientSession2 = WebClientSession.create(clientNoRedirects);
@@ -295,14 +337,14 @@ public class LzTool extends PanBase {
String pwd = shareLinkInfo.getSharePassword(); String pwd = shareLinkInfo.getSharePassword();
webClientSession.getAbs(sUrl).send().onSuccess(res -> { webClientSession.getAbs(sUrl).send().onSuccess(res -> {
String html = res.bodyAsString(); String html = asText(res);
// 检查是否需要 cookie 验证 // 检查是否需要 cookie 验证
if (html.contains("var arg1='")) { if (html.contains("var arg1='")) {
webClientSession = WebClientSession.create(clientNoRedirects); webClientSession = WebClientSession.create(clientNoRedirects);
setCookie(html); setCookie(html, sUrl);
// 重新请求 // 重新请求
webClientSession.getAbs(sUrl).send().onSuccess(res2 -> { webClientSession.getAbs(sUrl).send().onSuccess(res2 -> {
handleFileListParse(res2.bodyAsString(), pwd, sUrl, promise); handleFileListParse(asText(res2), pwd, sUrl, promise);
}).onFailure(err -> promise.fail(err)); }).onFailure(err -> promise.fail(err));
return; return;
} }
@@ -312,6 +354,11 @@ public class LzTool extends PanBase {
} }
private void handleFileListParse(String html, String pwd, String sUrl, Promise<List<FileInfo>> promise) { private void handleFileListParse(String html, String pwd, String sUrl, Promise<List<FileInfo>> promise) {
// 检测是否为文件分享链接 (不含 /s/、/b/ 路径段且不含 b0 开头的路径段)
if (!sUrl.matches(".*/(s|b)/[^/]+.*") && !sUrl.matches(".*/b0[^/]+.*")) {
promise.fail(baseMsg() + "该链接为蓝奏云文件分享,请使用文件解析接口");
return;
}
try { try {
String jsText = getJsByPwd(pwd, html, "var urls =window.location.href"); String jsText = getJsByPwd(pwd, html, "var urls =window.location.href");
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "file"); ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "file");
@@ -321,12 +368,12 @@ public class LzTool extends PanBase {
log.debug("解析参数: {}", map); log.debug("解析参数: {}", map);
MultiMap headers = getHeaders(sUrl); MultiMap headers = getHeaders(sUrl);
String url = SHARE_URL_PREFIX + "/filemoreajax.php?file=" + data.get("fid"); String url = SHARE_URL_PREFIX + "filemoreajax.php?file=" + data.get("fid");
webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> { webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
String resBody = asText(res2); String resBody = asText(res2);
// 再次检查是否需要 cookie 验证 // 再次检查是否需要 cookie 验证
if (resBody.contains("var arg1='")) { if (resBody.contains("var arg1='")) {
setCookie(resBody); setCookie(resBody, url);
// 重新请求 // 重新请求
webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res3 -> { webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res3 -> {
handleFileListResponse(asText(res3), promise); handleFileListResponse(asText(res3), promise);
@@ -335,7 +382,7 @@ public class LzTool extends PanBase {
} }
handleFileListResponse(resBody, promise); handleFileListResponse(resBody, promise);
}).onFailure(err -> promise.fail(err)); }).onFailure(err -> promise.fail(err));
} catch (ScriptException | NoSuchMethodException e) { } catch (ScriptException | NoSuchMethodException | RuntimeException e) {
promise.fail(e); promise.fail(e);
} }
} }
@@ -367,14 +414,20 @@ public class LzTool extends PanBase {
Long sizeNum = FileSizeConverter.convertToBytes(size); Long sizeNum = FileSizeConverter.convertToBytes(size);
String panType = shareLinkInfo.getType(); String panType = shareLinkInfo.getType();
String id = fileJson.getString("id"); String id = fileJson.getString("id");
fileInfo.setFileName(fileJson.getString("name_all")) String fileName = fileJson.getString("name_all");
// 构建 base64 参数,用于 /v2/redirectUrl 接口
JsonObject paramJson = new JsonObject()
.put("id", id)
.put("fileName", fileName);
String param = CommonUtils.urlBase64Encode(paramJson.encode());
fileInfo.setFileName(fileName)
.setFileId(id) .setFileId(id)
.setCreateTime(fileJson.getString("time")) .setCreateTime(fileJson.getString("time"))
.setFileType(fileJson.getString("icon")) .setFileType(fileJson.getString("icon"))
.setSizeStr(fileJson.getString("size")) .setSizeStr(fileJson.getString("size"))
.setSize(sizeNum) .setSize(sizeNum)
.setPanType(panType) .setPanType(panType)
.setParserUrl(getDomainName() + "/d/" + panType + "/" + id) .setParserUrl(String.format("%s/v2/redirectUrl/%s/%s", getDomainName(), panType, param))
.setPreviewUrl(String.format("%s/v2/view/%s/%s", getDomainName(), .setPreviewUrl(String.format("%s/v2/view/%s/%s", getDomainName(),
shareLinkInfo.getType(), id)); shareLinkInfo.getType(), id));
log.debug("文件信息: {}", fileInfo); log.debug("文件信息: {}", fileInfo);
@@ -386,6 +439,15 @@ public class LzTool extends PanBase {
} }
} }
@Override
public Future<String> parseById() {
JsonObject paramJson = (JsonObject) shareLinkInfo.getOtherParam().get("paramJson");
String id = paramJson.getString("id");
// 以文件ID重新构造标准访问URL复用 parse() 流程
shareLinkInfo.setStandardUrl(SHARE_URL_PREFIX + id);
return parse();
}
void setFileInfo(String html, ShareLinkInfo shareLinkInfo) { void setFileInfo(String html, ShareLinkInfo shareLinkInfo) {
// 写入 fileInfo // 写入 fileInfo
FileInfo fileInfo = new FileInfo(); FileInfo fileInfo = new FileInfo();
@@ -400,16 +462,17 @@ public class LzTool extends PanBase {
String fileId = CommonUtils.extract(html, Pattern.compile("\\?f=(.*?)&|fid = (.*?);")); String fileId = CommonUtils.extract(html, Pattern.compile("\\?f=(.*?)&|fid = (.*?);"));
String createTime = CommonUtils.extract(html, Pattern.compile(">上传时间:</span>(.*?)<")); String createTime = CommonUtils.extract(html, Pattern.compile(">上传时间:</span>(.*?)<"));
try { try {
long bytes = FileSizeConverter.convertToBytes(sizeStr);
fileInfo.setFileName(fileName) fileInfo.setFileName(fileName)
.setSize(bytes)
.setSizeStr(FileSizeConverter.convertToReadableSize(bytes))
.setCreateBy(createBy) .setCreateBy(createBy)
.setPanType(shareLinkInfo.getType()) .setPanType(shareLinkInfo.getType())
.setDescription(description) .setDescription(description)
.setFileType("file") .setFileType("file")
.setFileId(fileId) .setFileId(fileId)
.setCreateTime(createTime); .setCreateTime(createTime);
if (sizeStr != null && !sizeStr.isBlank()) {
long bytes = FileSizeConverter.convertToBytes(sizeStr);
fileInfo.setSize(bytes).setSizeStr(FileSizeConverter.convertToReadableSize(bytes));
}
} catch (Exception e) { } catch (Exception e) {
log.warn("文件信息解析异常", e); log.warn("文件信息解析异常", e);
} }

View File

@@ -7,11 +7,14 @@ import java.util.Arrays;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static java.util.regex.Pattern.compile; import static java.util.regex.Pattern.compile;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/** /**
* @author <a href="https://qaiu.top">QAIU</a> * @author <a href="https://qaiu.top">QAIU</a>
@@ -77,6 +80,55 @@ public class PanDomainTemplateTest {
} }
@Test
public void testWsPatternMatching() {
Pattern wsPattern = PanDomainTemplate.WS.getPattern();
// 历史域名
String[] positiveUrls = {
"https://f.ws59.cn/f/f25625rv6p6",
"https://f.ws28.cn/f/somekey123",
"https://www.wenshushu.cn/f/abc123",
// 新增域名
"https://www.wenxiaozhan.net/f/testkey1",
"https://www.wenxiaozhan.cn/f/testkey2",
"https://www.wss.show/f/testkey3",
"https://www.ws28.cn/f/testkey4",
"https://www.wss.email/f/testkey5",
"https://www.wss1.cn/f/testkey6",
"https://www.ws59.cn/f/testkey7",
"https://www.wss.cc/f/testkey8",
"https://www.wss.pet/f/testkey9",
"https://www.wss.ink/f/testkey10",
"https://www.wenxiaozhan.com/f/testkey11",
"https://www.wenshushu.com/f/testkey12",
"https://www.wss.zone/f/testkey13",
};
for (String url : positiveUrls) {
Matcher m = wsPattern.matcher(url);
assertTrue("WS pattern should match: " + url, m.matches());
assertNotNull("KEY group should not be null for: " + url, m.group("KEY"));
}
// 验证 KEY 提取正确性
Matcher m1 = wsPattern.matcher("https://f.ws59.cn/f/f25625rv6p6");
assertTrue(m1.matches());
assertEquals("f25625rv6p6", m1.group("KEY"));
Matcher m2 = wsPattern.matcher("https://www.wenshushu.cn/f/abc123");
assertTrue(m2.matches());
assertEquals("abc123", m2.group("KEY"));
// 负例:错误路径不匹配
assertFalse("Wrong path should not match",
wsPattern.matcher("https://www.wenshushu.cn/x/abc123").matches());
// 负例:非白名单域名不匹配
assertFalse("Non-whitelisted domain should not match",
wsPattern.matcher("https://www.evil.com/f/abc123").matches());
}
@Test @Test
public void verifyDuplicates() { public void verifyDuplicates() {

View File

@@ -74,7 +74,7 @@
<dependency> <dependency>
<groupId>cn.qaiu</groupId> <groupId>cn.qaiu</groupId>
<artifactId>parser</artifactId> <artifactId>parser</artifactId>
<version>10.2.3</version> <version>10.2.5</version>
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@@ -274,7 +274,7 @@
name: '联想乐云' name: '联想乐云'
}, },
fangcloud: { fangcloud: {
reg: /https:\/\/v2\.fangcloud\.(com|cn)\/(s|sharing)\/.+/, reg: /https:\/\/v2\.fangcloud\.(com|cn)\/(s|share|sharing)\/.+/,
host: /fangcloud\.(com|cn)/, host: /fangcloud\.(com|cn)/,
name: '亿方云' name: '亿方云'
}, },