From 40bbd3f1353a6fa65ea7c2f53828d0007d8f98ba Mon Sep 17 00:00:00 2001 From: BLSKWOLF <76619116+BLSKWOLF@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:36:39 +0800 Subject: [PATCH 1/5] =?UTF-8?q?[=E6=9C=AA=E5=AE=8C=E6=88=90]=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0QQ=E9=82=AE=E7=AE=B1=E6=96=87=E4=BB=B6=E8=A7=A3?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加QQ邮箱文件解析,暂时未解决cookie的问题 --- .../main/java/cn/qaiu/parser/IPanTool.java | 4 + .../main/java/cn/qaiu/parser/impl/QQTool.java | 181 ++++++++++++++++++ .../java/cn/qaiu/lz/web/http/ServerApi.java | 7 + 3 files changed, 192 insertions(+) create mode 100644 parser/src/main/java/cn/qaiu/parser/impl/QQTool.java diff --git a/parser/src/main/java/cn/qaiu/parser/IPanTool.java b/parser/src/main/java/cn/qaiu/parser/IPanTool.java index f554cf5..0dee55c 100644 --- a/parser/src/main/java/cn/qaiu/parser/IPanTool.java +++ b/parser/src/main/java/cn/qaiu/parser/IPanTool.java @@ -7,6 +7,7 @@ public interface IPanTool { Future parse(); static IPanTool typeMatching(String type, String key, String pwd) { + System.out.println(">>>>>>>> type: " + type + "\nkey: " + key + "\n pwd: " + pwd); return switch (type) { case "lz" -> new LzTool(key, pwd); case "cow" -> new CowTool(key, pwd); @@ -17,6 +18,7 @@ public interface IPanTool { case "fj" -> new FjTool(key, pwd); case "qk" -> new QkTool(key, pwd); case "le" -> new LeTool(key, pwd); + case "qq" -> new QQTool(key, pwd); default -> { throw new UnsupportedOperationException("未知分享类型"); } @@ -41,6 +43,8 @@ public interface IPanTool { return new LzTool(url, pwd); } else if (url.startsWith(LeTool.SHARE_URL_PREFIX)) { return new LeTool(url, pwd); + } else if (url.contains(QQTool.SHARE_URL_PREFIX)) { + return new QQTool(url, pwd); } throw new UnsupportedOperationException("未知分享类型"); diff --git a/parser/src/main/java/cn/qaiu/parser/impl/QQTool.java b/parser/src/main/java/cn/qaiu/parser/impl/QQTool.java new file mode 100644 index 0000000..e4a6a77 --- /dev/null +++ b/parser/src/main/java/cn/qaiu/parser/impl/QQTool.java @@ -0,0 +1,181 @@ +package cn.qaiu.parser.impl; + +import cn.qaiu.parser.IPanTool; +import cn.qaiu.parser.PanBase; +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.ext.web.client.WebClient; + +public class QQTool extends PanBase implements IPanTool { + + public static final String SHARE_URL_PREFIX = "wx.mail.qq.com/ftn/download?"; + + static String test = ""; + + public QQTool(String key, String pwd) { + super(key, pwd); + } + + @SuppressWarnings("unchecked") + public Future parse() { + + WebClient httpClient = this.client; + + // 补全链接 + if (!this.key.startsWith("https://" + SHARE_URL_PREFIX)) + { + if (this.key.startsWith(SHARE_URL_PREFIX)) + { + this.key = "https://" + this.key; + } + else if (this.key.startsWith("func=")) + { + this.key = "https://" + SHARE_URL_PREFIX + this.key; + } + else + { + // fail("未知分享链接: " + this.key); + throw new UnsupportedOperationException("未知分享类型"); + } + } + + // 设置基础HTTP头部 + var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " + + "like " + + "Gecko) Chrome/111.0.0.0 Mobile Safari/537.36"; + + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.set("User-Agent", userAgent2); + headers.set("sec-ch-ua-platform", "Android"); + headers.set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile"); + + // 获取下载中转站页面 + httpClient.getAbs(this.key).putHeaders(headers).send().onSuccess(res -> { + if (res.statusCode() == 200) + { + String html = res.bodyAsString(); + + // 匹配文件信息 + String filename = StringCutNot(html, "var filename = \"", "\""); + String filesize = StringCutNot(html, "var filesize = ", "\n"); + String url = StringCutNot(html, "var url = \"", "\""); + + if (filename != null && filesize != null && url != null) + { + // 设置所需HTTP头部 + headers.set("Referer", "https://" + StringCutNot(this.key, "https://", "/") + "/"); + headers.set("Host", StringCutNot(url, "https://", "/")); + res.headers().forEach((k, v) -> { + if (k.toLowerCase().equals("set-cookie")) + { + test = StringCutNot(v, "mail5k=", ";"); + headers.set("Cookie", "mail5k=" + StringCutNot(v, "mail5k=", ";") + ";"); + } + }); + + // 调试匹配的情况 + System.out.println("文件名称: " + filename); + System.out.println("文件大小: " + filesize); + System.out.println("文件直链: " + url); + System.out.println("mail5k= " + test); + + // 访问直链 + httpClient.getAbs(url).putHeaders(headers).send().onSuccess(res2 -> { + // 调试获取文件内容 + System.out.println("文件内容: " + res2.bodyAsString()); + + // 提交 + promise.complete(url); + }).onFailure(this.handleFail(this.key)); + + } + else + { + this.fail("匹配失败,可能是分享链接的方式已更新"); + } + } + else + { + this.fail("HTTP状态不正确,可能是分享链接的方式已更新"); + } + }).onFailure(this.handleFail(this.key)); + + return promise.future(); + } + + // 非贪婪截断匹配 + private String StringCutNot(final String strtarget, final String strstart, final String strend) + { + char[] target = strtarget.toCharArray(); + char[] start = strstart.toCharArray(); + char[] end = strend.toCharArray(); + + int startIdx = -1; + int endIdx = -1; + int targetLen = target.length; + int startLen = start.length; + int endLen = end.length; + + for (int i = 0; i <= targetLen - startLen; i++) + { + boolean match = true; + for (int j = 0; j < startLen; j++) + { + if (target[i + j] != start[j]) + { + match = false; + break; + } + } + if (match) + { + startIdx = i; + break; + } + } + + if (startIdx == -1) + { + return null; + } + + for (int i = startIdx + startLen; i <= targetLen - endLen; i++) + { + boolean match = true; + for (int j = 0; j < endLen; j++) + { + if (target[i + j] != end[j]) + { + match = false; + break; + } + } + if (match) + { + endIdx = i; + break; + } + } + + if (endIdx == -1) + { + return null; + } + + if (startIdx + startLen < endIdx) + { + StringBuilder strbuilder = new StringBuilder(); + + for (int i = startIdx + startLen; i < endIdx; i++) + { + strbuilder.append(target[i]); + } + + return strbuilder.toString(); + } + + return null; + } + +} diff --git a/web-service/src/main/java/cn/qaiu/lz/web/http/ServerApi.java b/web-service/src/main/java/cn/qaiu/lz/web/http/ServerApi.java index 9de193d..6643ac0 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/http/ServerApi.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/http/ServerApi.java @@ -2,6 +2,7 @@ package cn.qaiu.lz.web.http; import cn.qaiu.parser.IPanTool; import cn.qaiu.parser.impl.EcTool; +import cn.qaiu.parser.impl.QQTool; import cn.qaiu.vx.core.annotaions.RouteHandler; import cn.qaiu.vx.core.annotaions.RouteMapping; import cn.qaiu.vx.core.enums.RouteMethod; @@ -30,6 +31,12 @@ public class ServerApi { // 默认读取Url参数会被截断手动获取一下其他参数 url = EcTool.SHARE_URL_PREFIX + request.getParam("data"); } + if (url.contains(QQTool.SHARE_URL_PREFIX)) { + // 默认读取Url参数会被截断手动获取一下其他参数 + url = url + "&key=" + request.getParam("key") + + "&code=" + request.getParam("code") + "&k=" + request.getParam("k") + + "&fweb=" + request.getParam("fweb") + "&cl=" + request.getParam("cl"); + } IPanTool.shareURLPrefixMatching(url, pwd).parse().onSuccess(resUrl -> { ResponseUtil.redirect(response, resUrl, promise); }).onFailure(t -> promise.fail(t.fillInStackTrace())); From 632945d39a47818fffefe5db815e608aa3945a95 Mon Sep 17 00:00:00 2001 From: BLSKWOLF <76619116+BLSKWOLF@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:46:42 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index fa646df..7f486ee 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ 云盘解析服务 (nfd云解析) -预览地址 https://lz.qaiu.top +预览地址 https://lz.qaiu.top 注意: lz.qaiu.top因解析量过大IP已被123和小飞机禁止访问, 请不要过度依赖预览地址服务,建议本地搭建或者云服务器自行搭建 @@ -15,9 +15,9 @@ *重要声明:本项目仅供学习参考;请不要将此项目用于任何商业用途,否则可能带来严重的后果。* ## 网盘支持情况: -> 20230905 奶牛云直链做了防盗链,需加入请求头:Referer: https://cowtransfer.com/ -> 20230824 123云盘解析大文件(>100MB)失效,需要登录 -> 20230722 UC网盘解析失效,需要登录 +> 20230905 奶牛云直链做了防盗链,需加入请求头:Referer: https://cowtransfer.com/ +> 20230824 123云盘解析大文件(>100MB)失效,需要登录 +> 20230722 UC网盘解析失效,需要登录 `网盘名称(网盘标识):` @@ -44,6 +44,7 @@ - [X] 直链解析 - [文叔叔 (ws) 开发中](https://www.wenshushu.cn/) - [夸克网盘 (qk) 开发中](https://pan.quark.cn/) +- [QQ邮箱 (qq) 开发中](https://wx.mail.qq.com/) **TODO:** - 登录接口, 文件上传/下载/分享后端接口 @@ -57,24 +58,24 @@ Core模块集成Vert.x实现类似spring的注解式路由API API接口 ``` -网盘标识参考上面网盘支持情况, 括号内是可选内容: 表示当带有分享密码时需要加上密码参数 -parser接口可以直接解析分享链接: 加密分享需要加上参数pwd=密码; +网盘标识参考上面网盘支持情况, 括号内是可选内容: 表示当带有分享密码时需要加上密码参数 +parser接口可以直接解析分享链接: 加密分享需要加上参数pwd=密码; 其他接口在分享Key后面加上@密码; -1. 解析并自动302跳转 : +1. 解析并自动302跳转 : http(s)://your_host/parser?url=分享链接(&pwd=xxx) http(s)://your_host/网盘标识/分享key(@分享密码) 2. 获取解析后的直链--JSON格式 http(s)://your_host/json/parser?url=分享链接(&pwd=xxx) http(s)://your_host/json/网盘标识/分享key(@分享密码) -3. 特别注意的地方: +3. 特别注意的地方: - 有些网盘的加密分享的密码可以忽略: 如移动云空间,小飞机网盘 - 移动云空间(ec)使用parser?url= 解析时因为分享链接比较特殊(链接带有参数且含有#符号)所以要么对#进行转义%23要么直接去掉# 或者URL直接是主机名+'/'跟一个data参数 比如 http://your_host/parser?url=https://www.ecpan.cn/web//yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1 http://your_host/parser?url=https://www.ecpan.cn/web/%23/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1 http://your_host/parser?url=https://www.ecpan.cn/&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1 ``` -json返回数据格式示例: +json返回数据格式示例: ```json { "code": 200, @@ -148,8 +149,8 @@ GET http://127.0.0.1:6400/json/fc/e5079007dc31226096628870c7@QAIU ## 开发和打包 ```shell -# 环境要求: Jdk17 + maven; -mvn clean +# 环境要求: Jdk17 + maven; +mvn clean mvn package ``` @@ -159,28 +160,28 @@ mvn package ```shell cd ~ wget -O netdisk-fast-download.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.7/netdisk-fast-download.zip -unzip netdisk-fast-download-bin.zip +unzip netdisk-fast-download-bin.zip cd netdisk-fast-download bash service-install.sh ``` -服务相关命令: -1、查看服务状态 +服务相关命令: +1、查看服务状态 systemctl status netdisk-fast-download.service -2、控制服务 -启动服务 +2、控制服务 +启动服务 systemctl start netdisk-fast-download.service -重启服务 +重启服务 systemctl restart netdisk-fast-download.service -停止服务 +停止服务 systemctl stop netdisk-fast-download.service -开机启动服务 +开机启动服务 systemctl enable netdisk-fast-download.servic -停止开机启动 +停止开机启动 systemctl disable netdisk-fast-download.servic ## Windows服务部署 @@ -203,8 +204,8 @@ systemctl disable netdisk-fast-download.servic ## 支持该项目 -本项目长期维护如果觉得有帮助, 可以请作者喝杯咖啡, 感谢支持 -支付宝发大额红包了...就这几天, 不要错过哦 +本项目长期维护如果觉得有帮助, 可以请作者喝杯咖啡, 感谢支持 +支付宝发大额红包了...就这几天, 不要错过哦 ![image](https://github.com/qaiu/netdisk-fast-download/assets/29825328/54276aee-cc3f-4ebd-8973-2e15f6295819) [手机端支付宝打赏跳转链接](https://qr.alipay.com/fkx01882dnoxxtjenhlxt53) From 4b84ecd5fc6dce6271557d5a174f73df222f72f9 Mon Sep 17 00:00:00 2001 From: BLSKWOLF <76619116+BLSKWOLF@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:51:49 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E5=8F=94?= =?UTF-8?q?=E5=8F=94=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.修复QQ邮箱解析 2.添加文叔叔解析 --- README.md | 6 +- .../main/java/cn/qaiu/parser/IPanTool.java | 4 +- .../main/java/cn/qaiu/parser/impl/QQTool.java | 143 +++----------- .../main/java/cn/qaiu/parser/impl/WsTool.java | 183 ++++++++++++++++++ .../main/java/cn/qaiu/util/StringUtils.java | 36 ++++ 5 files changed, 253 insertions(+), 119 deletions(-) create mode 100644 parser/src/main/java/cn/qaiu/parser/impl/WsTool.java create mode 100644 parser/src/main/java/cn/qaiu/util/StringUtils.java diff --git a/README.md b/README.md index 7f486ee..c84ca8c 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,12 @@ - [ ] 登录, 上传, 下载, 分享 - [X] 直链解析 - [文叔叔 (ws) 开发中](https://www.wenshushu.cn/) -- [夸克网盘 (qk) 开发中](https://pan.quark.cn/) + - [ ] 登录, 上传, 下载, 分享 + - [X] 直链解析 - [QQ邮箱 (qq) 开发中](https://wx.mail.qq.com/) + - [ ] 登录, 上传, 下载, 分享 + - [*] 直链解析(用户无法直接使用直链) +- [夸克网盘 (qk) 开发中](https://pan.quark.cn/) **TODO:** - 登录接口, 文件上传/下载/分享后端接口 diff --git a/parser/src/main/java/cn/qaiu/parser/IPanTool.java b/parser/src/main/java/cn/qaiu/parser/IPanTool.java index 0dee55c..2ca2082 100644 --- a/parser/src/main/java/cn/qaiu/parser/IPanTool.java +++ b/parser/src/main/java/cn/qaiu/parser/IPanTool.java @@ -7,7 +7,6 @@ public interface IPanTool { Future parse(); static IPanTool typeMatching(String type, String key, String pwd) { - System.out.println(">>>>>>>> type: " + type + "\nkey: " + key + "\n pwd: " + pwd); return switch (type) { case "lz" -> new LzTool(key, pwd); case "cow" -> new CowTool(key, pwd); @@ -18,6 +17,7 @@ public interface IPanTool { case "fj" -> new FjTool(key, pwd); case "qk" -> new QkTool(key, pwd); case "le" -> new LeTool(key, pwd); + case "ws" -> new WsTool(key, pwd); case "qq" -> new QQTool(key, pwd); default -> { throw new UnsupportedOperationException("未知分享类型"); @@ -43,6 +43,8 @@ public interface IPanTool { return new LzTool(url, pwd); } else if (url.startsWith(LeTool.SHARE_URL_PREFIX)) { return new LeTool(url, pwd); + } else if (url.contains(WsTool.SHARE_URL_PREFIX) || url.contains(WsTool.SHARE_URL_PREFIX2)) { + return new WsTool(url, pwd); } else if (url.contains(QQTool.SHARE_URL_PREFIX)) { return new QQTool(url, pwd); } diff --git a/parser/src/main/java/cn/qaiu/parser/impl/QQTool.java b/parser/src/main/java/cn/qaiu/parser/impl/QQTool.java index e4a6a77..d50c34b 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/QQTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/QQTool.java @@ -1,17 +1,23 @@ package cn.qaiu.parser.impl; +import cn.qaiu.util.StringUtils; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + import cn.qaiu.parser.IPanTool; import cn.qaiu.parser.PanBase; import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.ext.web.client.WebClient; +/** + * QQ邮箱 + */ public class QQTool extends PanBase implements IPanTool { public static final String SHARE_URL_PREFIX = "wx.mail.qq.com/ftn/download?"; - static String test = ""; - public QQTool(String key, String pwd) { super(key, pwd); } @@ -22,19 +28,12 @@ public class QQTool extends PanBase implements IPanTool { WebClient httpClient = this.client; // 补全链接 - if (!this.key.startsWith("https://" + SHARE_URL_PREFIX)) - { - if (this.key.startsWith(SHARE_URL_PREFIX)) - { + if (!this.key.startsWith("https://" + SHARE_URL_PREFIX)) { + if (this.key.startsWith(SHARE_URL_PREFIX)) { this.key = "https://" + this.key; - } - else if (this.key.startsWith("func=")) - { + } else if (this.key.startsWith("func=")) { this.key = "https://" + SHARE_URL_PREFIX + this.key; - } - else - { - // fail("未知分享链接: " + this.key); + } else { throw new UnsupportedOperationException("未知分享类型"); } } @@ -52,51 +51,35 @@ public class QQTool extends PanBase implements IPanTool { // 获取下载中转站页面 httpClient.getAbs(this.key).putHeaders(headers).send().onSuccess(res -> { - if (res.statusCode() == 200) - { + if (res.statusCode() == 200) { String html = res.bodyAsString(); // 匹配文件信息 - String filename = StringCutNot(html, "var filename = \"", "\""); - String filesize = StringCutNot(html, "var filesize = ", "\n"); - String url = StringCutNot(html, "var url = \"", "\""); + String filename = StringUtils.StringCutNot(html, "var filename = \"", "\""); + String filesize = StringUtils.StringCutNot(html, "var filesize = ", "\n"); + String fileurl = StringUtils.StringCutNot(html, "var url = \"", "\""); - if (filename != null && filesize != null && url != null) - { + if (filename != null && filesize != null && fileurl != null) { // 设置所需HTTP头部 - headers.set("Referer", "https://" + StringCutNot(this.key, "https://", "/") + "/"); - headers.set("Host", StringCutNot(url, "https://", "/")); + headers.set("Referer", "https://" + StringUtils.StringCutNot(this.key, "https://", "/") + "/"); + headers.set("Host", StringUtils.StringCutNot(fileurl, "https://", "/")); res.headers().forEach((k, v) -> { - if (k.toLowerCase().equals("set-cookie")) - { - test = StringCutNot(v, "mail5k=", ";"); - headers.set("Cookie", "mail5k=" + StringCutNot(v, "mail5k=", ";") + ";"); + if (k.toLowerCase().equals("set-cookie")) { + headers.set("Cookie", "mail5k=" + StringUtils.StringCutNot(v, "mail5k=", ";") + ";"); } }); // 调试匹配的情况 System.out.println("文件名称: " + filename); System.out.println("文件大小: " + filesize); - System.out.println("文件直链: " + url); - System.out.println("mail5k= " + test); + System.out.println("文件直链: " + fileurl); - // 访问直链 - httpClient.getAbs(url).putHeaders(headers).send().onSuccess(res2 -> { - // 调试获取文件内容 - System.out.println("文件内容: " + res2.bodyAsString()); - - // 提交 - promise.complete(url); - }).onFailure(this.handleFail(this.key)); - - } - else - { + // 提交 + promise.complete(fileurl.replace("\\x26", "&")); + } else { this.fail("匹配失败,可能是分享链接的方式已更新"); } - } - else - { + } else { this.fail("HTTP状态不正确,可能是分享链接的方式已更新"); } }).onFailure(this.handleFail(this.key)); @@ -104,78 +87,4 @@ public class QQTool extends PanBase implements IPanTool { return promise.future(); } - // 非贪婪截断匹配 - private String StringCutNot(final String strtarget, final String strstart, final String strend) - { - char[] target = strtarget.toCharArray(); - char[] start = strstart.toCharArray(); - char[] end = strend.toCharArray(); - - int startIdx = -1; - int endIdx = -1; - int targetLen = target.length; - int startLen = start.length; - int endLen = end.length; - - for (int i = 0; i <= targetLen - startLen; i++) - { - boolean match = true; - for (int j = 0; j < startLen; j++) - { - if (target[i + j] != start[j]) - { - match = false; - break; - } - } - if (match) - { - startIdx = i; - break; - } - } - - if (startIdx == -1) - { - return null; - } - - for (int i = startIdx + startLen; i <= targetLen - endLen; i++) - { - boolean match = true; - for (int j = 0; j < endLen; j++) - { - if (target[i + j] != end[j]) - { - match = false; - break; - } - } - if (match) - { - endIdx = i; - break; - } - } - - if (endIdx == -1) - { - return null; - } - - if (startIdx + startLen < endIdx) - { - StringBuilder strbuilder = new StringBuilder(); - - for (int i = startIdx + startLen; i < endIdx; i++) - { - strbuilder.append(target[i]); - } - - return strbuilder.toString(); - } - - return null; - } - } diff --git a/parser/src/main/java/cn/qaiu/parser/impl/WsTool.java b/parser/src/main/java/cn/qaiu/parser/impl/WsTool.java new file mode 100644 index 0000000..cfdf06c --- /dev/null +++ b/parser/src/main/java/cn/qaiu/parser/impl/WsTool.java @@ -0,0 +1,183 @@ +package cn.qaiu.parser.impl; + +import cn.qaiu.util.StringUtils; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +import cn.qaiu.parser.IPanTool; +import cn.qaiu.parser.PanBase; +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.core.json.DecodeException; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.client.WebClient; + +/** + * 文叔叔 + */ +public class WsTool extends PanBase implements IPanTool { + + public static final String SHARE_URL_PREFIX = "www.wenshushu.cn/f/"; + public static final String SHARE_URL_PREFIX2 = "f.ws59.cn/f/"; + public static final String SHARE_URL_API = "https://www.wenshushu.cn/ap/"; + + public WsTool(String key, String pwd) { + super(key, pwd); + } + + @SuppressWarnings("unchecked") + public Future parse() { + + WebClient httpClient = this.client; + + // 补全链接 + if (!this.key.startsWith("https://" + SHARE_URL_PREFIX) && !this.key.startsWith("https://" + SHARE_URL_PREFIX2)) { + if (this.key.startsWith(SHARE_URL_PREFIX) || this.key.startsWith(SHARE_URL_PREFIX2)) { + this.key = "https://" + this.key; + } else if (this.key.matches("^[A-Za-z0-9]+$")) { + this.key = "https://" + SHARE_URL_PREFIX + this.key; + } else { + throw new UnsupportedOperationException("未知分享类型"); + } + } + + // 设置基础HTTP头部 + var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " + + "like " + + "Gecko) Chrome/111.0.0.0 Mobile Safari/537.36"; + + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.set("User-Agent", userAgent2); + headers.set("sec-ch-ua-platform", "Android"); + headers.set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"); + headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile"); + + // 获取匿名登录token + httpClient.postAbs(SHARE_URL_API + "login/anonymous").putHeaders(headers) + .sendJsonObject(JsonObject.of("dev_info", "{}")) + .onSuccess(res -> { + + if (res.statusCode() == 200) { + try { + // 设置匿名登录token + String token = res.bodyAsJsonObject().getJsonObject("data").getString("token"); + headers.set("X-Token", token); + + // 获取文件夹信息 + httpClient.postAbs(SHARE_URL_API + "task/mgrtask").putHeaders(headers) + .sendJsonObject(JsonObject.of( + "tid", StringUtils.StringCutNot(key, this.key.startsWith(SHARE_URL_PREFIX) ? SHARE_URL_PREFIX : SHARE_URL_PREFIX2), + "password", "" + )).onSuccess(res2 -> { + + if (res2.statusCode() == 200) { + try { + // 获取文件夹信息 + String filetime = res2.bodyAsJsonObject().getJsonObject("data").getString("expire"); // 文件夹剩余时间 + String filesize = res2.bodyAsJsonObject().getJsonObject("data").getString("file_size"); // 文件夹大小 + String filepid = res2.bodyAsJsonObject().getJsonObject("data").getString("ufileid"); // 文件夹pid + String filebid = res2.bodyAsJsonObject().getJsonObject("data").getString("boxid"); // 文件夹bid + + // 调试输出文件夹信息 + System.out.println("文件夹期限: " + filetime); + System.out.println("文件夹大小: " + filesize); + System.out.println("文件夹pid: " + filepid); + System.out.println("文件夹bid: " + filebid); + + // 获取文件信息 + httpClient.postAbs(SHARE_URL_API + "ufile/list").putHeaders(headers) + .sendJsonObject(JsonObject.of( + "start", 0, + "sort", JsonObject.of( + "name", "asc" + ), + "bid", filebid, + "pid", filepid, + "type", 1, + "options", JsonObject.of( + "uploader", "true" + ), + "size", 50 + )).onSuccess(res3 -> { + + if (res3.statusCode() == 200) { + try { + // 获取文件信息 + String filename = res3.bodyAsJsonObject().getJsonObject("data") + .getJsonArray("fileList").getJsonObject(0).getString("fname"); // 文件名称 + String filefid = res3.bodyAsJsonObject().getJsonObject("data") + .getJsonArray("fileList").getJsonObject(0).getString("fid"); // 文件fid + + // 调试输出文件信息 + System.out.println("文件名称: " + filename); + System.out.println("文件fid: " + filefid); + + // 检查文件是否失效 + httpClient.postAbs(SHARE_URL_API + "dl/sign").putHeaders(headers) + .sendJsonObject(JsonObject.of( + "consumeCode", 0, + "type", 1, + "ufileid", filefid + )).onSuccess(res4 -> { + + if (res4.statusCode() == 200) { + try { + // 获取直链 + String fileurl = res4.bodyAsJsonObject().getJsonObject("data").getString("url"); + + // 调试输出文件直链 + System.out.println("文件直链: " + fileurl); + + if (!fileurl.equals("")) + { + try { + promise.complete(URLDecoder.decode(fileurl, "UTF-8")); + } catch (UnsupportedEncodingException e) { + promise.complete(fileurl); + } + } + else + { + this.fail("文件已失效"); + } + + } catch (DecodeException | NullPointerException e) { + this.fail("获取文件信息失败,可能是分享链接的方式已更新,或者对方的文件已失效"); + } + } else { + this.fail("HTTP状态不正确,可能是分享链接的方式已更新"); + } + + }); + + } catch (DecodeException | NullPointerException e) { + this.fail("获取文件信息失败,可能是分享链接的方式已更新"); + } + } else { + this.fail("HTTP状态不正确,可能是分享链接的方式已更新"); + } + + }); + + } catch (DecodeException | NullPointerException e) { + this.fail("获取文件夹信息失败,可能是分享链接的方式已更新"); + } + } else { + this.fail("HTTP状态不正确,可能是分享链接的方式已更新"); + } + + }).onFailure(this.handleFail(this.key)); + + } catch (DecodeException | NullPointerException e) { + this.fail("token获取失败,可能是分享链接的方式已更新"); + } + } else { + this.fail("HTTP状态不正确,可能是分享链接的方式已更新"); + } + + }).onFailure(this.handleFail(this.key)); + + return promise.future(); + } +} diff --git a/parser/src/main/java/cn/qaiu/util/StringUtils.java b/parser/src/main/java/cn/qaiu/util/StringUtils.java new file mode 100644 index 0000000..5b324fb --- /dev/null +++ b/parser/src/main/java/cn/qaiu/util/StringUtils.java @@ -0,0 +1,36 @@ +package cn.qaiu.util; + +public class StringUtils { + + // 非贪婪截断匹配 + public static String StringCutNot(final String strtarget, final String strstart) + { + int startIdx = strtarget.indexOf(strstart); + + if (startIdx != -1) { + startIdx += strstart.length(); + return strtarget.substring(startIdx); + } + + return null; + } + + // 非贪婪截断匹配 + public static String StringCutNot(final String strtarget, final String strstart, final String strend) + { + int startIdx = strtarget.indexOf(strstart); + int endIdx = -1; + + if (startIdx != -1) { + startIdx += strstart.length(); + endIdx = strtarget.indexOf(strend, startIdx); + + if (endIdx != -1) { + return strtarget.substring(startIdx, endIdx); + } + } + + return null; + } + +} From 72f363d0e62ba81ea9d0c5f3ec996af321c7f2a8 Mon Sep 17 00:00:00 2001 From: BLSKWOLF <76619116+BLSKWOLF@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:56:10 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c84ca8c..b0d263b 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ - [X] 直链解析 - [QQ邮箱 (qq) 开发中](https://wx.mail.qq.com/) - [ ] 登录, 上传, 下载, 分享 - - [*] 直链解析(用户无法直接使用直链) + - [X] 直链解析(用户无法直接使用直链) - [夸克网盘 (qk) 开发中](https://pan.quark.cn/) **TODO:** From a682d2d876366625010f774273bbf3eb893ec013 Mon Sep 17 00:00:00 2001 From: qaiu <736226400@qq.com> Date: Fri, 15 Dec 2023 10:14:00 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E5=8A=A0=E5=85=A5Jansi=20=E8=AE=A9windows?= =?UTF-8?q?=20cmd=E6=97=A5=E5=BF=97=E5=BD=A9=E8=89=B2=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web-service/pom.xml | 6 ++++++ web-service/src/main/resources/logback.xml | 1 + 2 files changed, 7 insertions(+) diff --git a/web-service/pom.xml b/web-service/pom.xml index 591b4f3..f1ebe58 100644 --- a/web-service/pom.xml +++ b/web-service/pom.xml @@ -36,6 +36,12 @@ logback-classic ${logback.version} + + + org.fusesource.jansi + jansi + 1.17.1 + org.slf4j slf4j-api diff --git a/web-service/src/main/resources/logback.xml b/web-service/src/main/resources/logback.xml index dda2ef4..4b05965 100644 --- a/web-service/src/main/resources/logback.xml +++ b/web-service/src/main/resources/logback.xml @@ -46,6 +46,7 @@ + true ${CUSTOMER_PATTERN2}