mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-07-13 06:51:14 +00:00
Compare commits
367 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97ae1a5e92 | |||
| a4a521a6f8 | |||
| 2056a91071 | |||
| f6209a8959 | |||
| 72ed0ea8f8 | |||
| d698f82299 | |||
| ba4666c32a | |||
| d6d37e8204 | |||
| cb9dbfcc69 | |||
| 42b366ed0f | |||
| 4021c507b6 | |||
| 4d8e82080d | |||
| fca608b44e | |||
| 34cb89a6ea | |||
| 69d5f269bd | |||
| 459c974cb8 | |||
| 2f22cb01eb | |||
| e17fb99de4 | |||
| 0f926a57ef | |||
| 4380bfe0d6 | |||
| 8127cd0758 | |||
| 71a220f42b | |||
| d3b02676ec | |||
| d8f0dc4f8e | |||
| 48aa5b6148 | |||
| a989841a89 | |||
| 86783e8e46 | |||
| 66b9bcc53a | |||
| ff08615d1e | |||
| 4a6c3a1f90 | |||
| c79702eba8 | |||
| 41fc935c09 | |||
| 5fbbe5b240 | |||
| 9c121c03f2 | |||
| b74c3f31c4 | |||
| f23b97e22c | |||
| 0560989e77 | |||
| f2c9c34324 | |||
| a97268c702 | |||
| 2654b550fb | |||
| 12a5a17a30 | |||
| e346812c0a | |||
| 6b2e391af9 | |||
| 199456cb11 | |||
| 636994387f | |||
| 90c79f7bac | |||
| 79601b36a5 | |||
| 96cef89f08 | |||
| e057825b25 | |||
| ebe848dfe8 | |||
| e259a0989e | |||
| f750aa68e8 | |||
| 49b8501e86 | |||
| fc2e2a4697 | |||
| b4b1d7f923 | |||
| df646b8c43 | |||
| 8e790f6b22 | |||
| 2e76af980e | |||
| 80ccbe5b62 | |||
| aa0cd68f7f | |||
| 51833148b1 | |||
| 0fa77ebf21 | |||
| 584c075930 | |||
| 9e7a3718a4 | |||
| 0e2ca2f1ca | |||
| 52e889333b | |||
| 4745440079 | |||
| b5628eac17 | |||
| d23b11577e | |||
| f1dd9fc0ee | |||
| 0877fadcfb | |||
| 733059dc8e | |||
| 321380c2b9 | |||
| deb121a51b | |||
| b6aef7c239 | |||
| b13a7a5ee1 | |||
| fff6a00690 | |||
| b4da3cee20 | |||
| 0a650996a1 | |||
| 37b91cd388 | |||
| 42b721eabf | |||
| 231d5c3fb9 | |||
| 064efdf3f3 | |||
| 7b364a0f90 | |||
| c8a4ca7f16 | |||
| 97627b824c | |||
| 6dbdc9bd90 | |||
| 4166ea10af | |||
| fa12ab2c51 | |||
| 4fc4ed8640 | |||
| 48172f2769 | |||
| c7e6d68fbd | |||
| e6672a51c5 | |||
| abde7841ac | |||
| 8e661ed1c5 | |||
| 217cb3a776 | |||
| b8c1bca900 | |||
| 5e09b8e92a | |||
| c16bde6bb8 | |||
| eb06eb9f3d | |||
| 0c49088098 | |||
| b970241a64 | |||
| 6c5aafc11e | |||
| ca0846f4a7 | |||
| 14f7fcc5ad | |||
| 23a18aba5c | |||
| 2d5a79bb16 | |||
| 51e1bbefbb | |||
| 6647fc5371 | |||
| b67544f0cd | |||
| ef5826a73b | |||
| a48adbd0df | |||
| 5c60493a24 | |||
| 55e6227de0 | |||
| 24a7395004 | |||
| b2a7187fc5 | |||
| ace7cdc88e | |||
| 2e909b5868 | |||
| de78bcbc98 | |||
| c560f0e902 | |||
| 88860c9302 | |||
| ef65d0e095 | |||
| 6438505f4a | |||
| 1be5030dd1 | |||
| 421b2f4a42 | |||
| a66bf84381 | |||
| 0c4d366d6d | |||
| a1d0a921fa | |||
| 2092230a61 | |||
| 6e5ae6eff3 | |||
| 4f8259d772 | |||
| 8b987d9824 | |||
| e8ba451d18 | |||
| 77758db463 | |||
| 6c58598a8e | |||
| 3ac35230a3 | |||
| ca91302d28 | |||
| e07272a5dc | |||
| 461305e1df | |||
| 8e8ab10a0f | |||
| e754326925 | |||
| 4c92994c6f | |||
| 66c57f47ac | |||
| ec689eadd8 | |||
| c1e15709a7 | |||
| 2848937ce7 | |||
| 42ff0c21b2 | |||
| 3ed7e547e6 | |||
| fad8e688df | |||
| b2f2dcac4c | |||
| fcba78e977 | |||
| 77c9d777a1 | |||
| 4460659210 | |||
| 8631524107 | |||
| 0579588814 | |||
| df2bfb6ac7 | |||
| 517b6f8910 | |||
| 94a46d2833 | |||
| 1631a0faa1 | |||
| 06d5943cb6 | |||
| 3095e13676 | |||
| 482cbce7e8 | |||
| ef2fc3ab98 | |||
| 5b57b05eae | |||
| 093579c6f5 | |||
| c2d4990d7f | |||
| 40e8380738 | |||
| b716e1e861 | |||
| 8432d4952c | |||
| dd8f085f63 | |||
| 161ff8d8a3 | |||
| 1390cd0104 | |||
| 7a02b1e97f | |||
| 036f107c90 | |||
| 5652383450 | |||
| 9a047a5da0 | |||
| 8975743a37 | |||
| 0e30eafe49 | |||
| 7facb62f21 | |||
| 30d43cb961 | |||
| c505b17e35 | |||
| 080c4c753d | |||
| ade0d34d91 | |||
| 56d082eb0b | |||
| 795c4529ba | |||
| 0f5cfe22ea | |||
| 925ad2c3a5 | |||
| f3e96907fe | |||
| 75a1e58a7d | |||
| 379e889f71 | |||
| 40c06f397b | |||
| 9e9302436e | |||
| 6d816d4193 | |||
| 438eda9c08 | |||
| ace39e4633 | |||
| 7712391f29 | |||
| 65f08dcb02 | |||
| 1d332aa6f4 | |||
| ba81641517 | |||
| fb30bdb879 | |||
| fc451d3b41 | |||
| ffee1f3462 | |||
| f30027dd13 | |||
| 8b6aad17f4 | |||
| b77930adfb | |||
| aff8f88076 | |||
| 4e6582e24c | |||
| fa9acaccfd | |||
| 0414f85f12 | |||
| 527dd0eeb4 | |||
| 74ed7475c9 | |||
| 54dc3dba96 | |||
| 9980159090 | |||
| 0b193ebb00 | |||
| f5fc9843b2 | |||
| df1f67dd26 | |||
| b069a5f576 | |||
| 7686763a03 | |||
| 635a6eac37 | |||
| 877edc535f | |||
| 01d59e3c1e | |||
| fece2799e3 | |||
| de9756ee86 | |||
| 51f047a51b | |||
| 04b66e82b7 | |||
| df89253647 | |||
| 45dbca794e | |||
| 857bf28f99 | |||
| e07ce15228 | |||
| 0637bcfd8e | |||
| 23db0563ac | |||
| ccba71aa4e | |||
| fee4bf2ad6 | |||
| 5052fea9ef | |||
| e85215fca1 | |||
| e42fe45329 | |||
| 4240815bd1 | |||
| 6f0c5305e2 | |||
| 757005cad8 | |||
| 81651ad97c | |||
| f3763b6058 | |||
| 82478dc485 | |||
| 703fd05d43 | |||
| ff868b6e2a | |||
| 051a74b37b | |||
| a0a1085623 | |||
| 2612d3919c | |||
| 6f123a236f | |||
| 71e57e6a08 | |||
| 7cb18d8186 | |||
| cdbf670ece | |||
| e0dafee617 | |||
| c37bce1563 | |||
| 0b3c77d644 | |||
| 2cf85caf86 | |||
| 594010ba88 | |||
| d91460d2e2 | |||
| 89713e6ac9 | |||
| 17c9b2538c | |||
| d337b003cb | |||
| 8f1485656b | |||
| f0c4ec3031 | |||
| 458be84aca | |||
| c7716aad34 | |||
| 4a3e734408 | |||
| 54cc212753 | |||
| f4ae1eaa51 | |||
| d2537282c9 | |||
| 87527688c3 | |||
| 2be0b6505a | |||
| 672f100c7c | |||
| 5af402c0c5 | |||
| 693a4f0f63 | |||
| f8d2426ff6 | |||
| 973a9bedcd | |||
| a583733400 | |||
| 78eb51b3ca | |||
| a2606be9d8 | |||
| a4975c72ce | |||
| 58f96822a4 | |||
| 96b0d94986 | |||
| 70b38db8c5 | |||
| b6a9c2d3a0 | |||
| a01df6c7db | |||
| 4455bee570 | |||
| cd0adef2ed | |||
| 4aa24a65fb | |||
| 760dca8772 | |||
| 8269673619 | |||
| 82ec586554 | |||
| ca98cc8708 | |||
| f07800985d | |||
| b042df93b7 | |||
| ecf4441946 | |||
| 39b2612840 | |||
| 218f486e6b | |||
| cfcc25f175 | |||
| 155e88223c | |||
| 05039ece51 | |||
| 1c673f2b46 | |||
| 2232a70228 | |||
| e661b1d817 | |||
| 5a6a65f580 | |||
| 5cdd3bcd30 | |||
| 1233a885b8 | |||
| adf56cd768 | |||
| cd4b208be9 | |||
| 502de1a5d0 | |||
| 4158f869a3 | |||
| ff569d339c | |||
| 10eec323dd | |||
| 0a3db51c7d | |||
| 229aee0b30 | |||
| 44714aa981 | |||
| 2b6138a889 | |||
| 5e424f7bf4 | |||
| 294e47deed | |||
| dc42547b73 | |||
| 7ef7f0706b | |||
| a59b98a7c9 | |||
| 088fee9a4d | |||
| d8666acfe8 | |||
| 209e9c2866 | |||
| 6c3195dea4 | |||
| 7d774a7433 | |||
| f1ec4433cf | |||
| 1f825db261 | |||
| 1019f24f1d | |||
| f5c5b99579 | |||
| e002d19f1b | |||
| 0d5c9651f0 | |||
| 53fc13b95c | |||
| 694c3b0ddc | |||
| 9b3d4577cc | |||
| 77783915dd | |||
| b67ac21a79 | |||
| 603afed2f2 | |||
| c2a7c34496 | |||
| edd40f48ba | |||
| cca3d6b8b9 | |||
| f004512903 | |||
| 6407bb6730 | |||
| b914eeadec | |||
| dcadc6783e | |||
| bc9f43634f | |||
| 4778f0164c | |||
| 9904754a07 | |||
| 1b79077c9e | |||
| c13afb05b3 | |||
| 03e320efb8 | |||
| 7846332476 | |||
| 2d5d3b86e0 | |||
| 7c9ba890af | |||
| 0d609daffa | |||
| c12e56d402 | |||
| c7b38c07d5 | |||
| dc51066cea | |||
| 59d2fb3010 | |||
| a0fe702c10 | |||
| f886f7e366 | |||
| 1d475d88ed | |||
| e64c901912 | |||
| 5fce02e623 | |||
| 13997bc543 | |||
| 3e05b0d6f9 | |||
| 966417f867 | |||
| 601a0d1b91 |
Vendored
-52
@@ -1,7 +1,4 @@
|
|||||||
{
|
{
|
||||||
// 使用 IntelliSense 了解相关属性。
|
|
||||||
// 悬停以查看现有属性的描述。
|
|
||||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
@@ -10,61 +7,12 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "${file}"
|
"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",
|
"type": "java",
|
||||||
"name": "AppMain",
|
"name": "AppMain",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "cn.qaiu.lz.AppMain",
|
"mainClass": "cn.qaiu.lz.AppMain",
|
||||||
"projectName": "web-service"
|
"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 网盘分享链接云解析服务
|
# netdisk-fast-download 网盘分享链接云解析服务
|
||||||
QQ群:1017480890
|
QQ交流群:1017480890
|
||||||
|
|
||||||
netdisk-fast-download网盘直链云解析(nfd云解析)能把网盘分享下载链接转化为直链,支持多款云盘,已支持蓝奏云/蓝奏云优享/奶牛快传/移动云云空间/小飞机盘/亿方云/123云盘/Cloudreve等,支持加密分享,以及部分网盘文件夹分享。
|
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)
|
**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)
|
[预览地址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返回数据格式示例**
|
||||||
@@ -88,13 +88,17 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
|
|||||||
- Onedrive-pod
|
- Onedrive-pod
|
||||||
- Dropbox-pdp
|
- Dropbox-pdp
|
||||||
- iCloud-pic
|
- iCloud-pic
|
||||||
### 仅专属版提供
|
### 专属版提供
|
||||||
|
- [夸克云盘-qk](https://pan.quark.cn/)
|
||||||
|
- [UC云盘-uc](https://fast.uc.cn/)
|
||||||
- [移动云盘-p139](https://yun.139.com/)
|
- [移动云盘-p139](https://yun.139.com/)
|
||||||
- [联通云盘-pwo](https://pan.wo.cn/)
|
- [联通云盘-pwo](https://pan.wo.cn/)
|
||||||
- [天翼云盘-p189](https://cloud.189.cn/)
|
- [天翼云盘-p189](https://cloud.189.cn/)
|
||||||
|
|
||||||
## API接口
|
## API接口
|
||||||
|
|
||||||
|
[api接口文档](https://nfdparser.apifox.cn/)
|
||||||
|
|
||||||
### 服务端口
|
### 服务端口
|
||||||
- **6400**: API 服务端口(建议使用 Nginx 代理)
|
- **6400**: API 服务端口(建议使用 Nginx 代理)
|
||||||
- **6401**: 内置 Web 解析工具(个人使用可直接开放此端口)
|
- **6401**: 内置 Web 解析工具(个人使用可直接开放此端口)
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
package cn.qaiu.vx.core.verticle.conf;
|
|
||||||
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
import io.vertx.core.json.JsonArray;
|
|
||||||
import io.vertx.core.json.impl.JsonUtil;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converter and mapper for {@link cn.qaiu.vx.core.verticle.conf.HttpProxyConf}.
|
|
||||||
* NOTE: This class has been automatically generated from the {@link cn.qaiu.vx.core.verticle.conf.HttpProxyConf} original class using Vert.x codegen.
|
|
||||||
*/
|
|
||||||
public class HttpProxyConfConverter {
|
|
||||||
|
|
||||||
|
|
||||||
private static final Base64.Decoder BASE64_DECODER = JsonUtil.BASE64_DECODER;
|
|
||||||
private static final Base64.Encoder BASE64_ENCODER = JsonUtil.BASE64_ENCODER;
|
|
||||||
|
|
||||||
static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, HttpProxyConf obj) {
|
|
||||||
for (java.util.Map.Entry<String, Object> member : json) {
|
|
||||||
switch (member.getKey()) {
|
|
||||||
case "password":
|
|
||||||
if (member.getValue() instanceof String) {
|
|
||||||
obj.setPassword((String)member.getValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "port":
|
|
||||||
if (member.getValue() instanceof Number) {
|
|
||||||
obj.setPort(((Number)member.getValue()).intValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "preProxyOptions":
|
|
||||||
if (member.getValue() instanceof JsonObject) {
|
|
||||||
obj.setPreProxyOptions(new io.vertx.core.net.ProxyOptions((io.vertx.core.json.JsonObject)member.getValue()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "timeout":
|
|
||||||
if (member.getValue() instanceof Number) {
|
|
||||||
obj.setTimeout(((Number)member.getValue()).intValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "username":
|
|
||||||
if (member.getValue() instanceof String) {
|
|
||||||
obj.setUsername((String)member.getValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toJson(HttpProxyConf obj, JsonObject json) {
|
|
||||||
toJson(obj, json.getMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toJson(HttpProxyConf obj, java.util.Map<String, Object> json) {
|
|
||||||
if (obj.getPassword() != null) {
|
|
||||||
json.put("password", obj.getPassword());
|
|
||||||
}
|
|
||||||
if (obj.getPort() != null) {
|
|
||||||
json.put("port", obj.getPort());
|
|
||||||
}
|
|
||||||
if (obj.getPreProxyOptions() != null) {
|
|
||||||
json.put("preProxyOptions", obj.getPreProxyOptions().toJson());
|
|
||||||
}
|
|
||||||
if (obj.getTimeout() != null) {
|
|
||||||
json.put("timeout", obj.getTimeout());
|
|
||||||
}
|
|
||||||
if (obj.getUsername() != null) {
|
|
||||||
json.put("username", obj.getUsername());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package cn.qaiu.vx.core.verticle.conf;
|
|
||||||
|
|
||||||
import io.vertx.codegen.annotations.DataObject;
|
|
||||||
import io.vertx.codegen.json.annotations.JsonGen;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
import io.vertx.core.net.ProxyOptions;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@DataObject
|
|
||||||
@JsonGen(publicConverter = false)
|
|
||||||
public class HttpProxyConf {
|
|
||||||
|
|
||||||
public static final String DEFAULT_USERNAME = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
public static final String DEFAULT_PASSWORD = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
public static final Integer DEFAULT_PORT = 6402;
|
|
||||||
|
|
||||||
public static final Integer DEFAULT_TIMEOUT = 15000;
|
|
||||||
|
|
||||||
Integer timeout;
|
|
||||||
|
|
||||||
String username;
|
|
||||||
|
|
||||||
String password;
|
|
||||||
|
|
||||||
Integer port;
|
|
||||||
|
|
||||||
ProxyOptions preProxyOptions;
|
|
||||||
|
|
||||||
public HttpProxyConf() {
|
|
||||||
this.username = DEFAULT_USERNAME;
|
|
||||||
this.password = DEFAULT_PASSWORD;
|
|
||||||
this.timeout = DEFAULT_PORT;
|
|
||||||
this.timeout = DEFAULT_TIMEOUT;
|
|
||||||
this.preProxyOptions = new ProxyOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf(JsonObject json) {
|
|
||||||
this();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Integer getTimeout() {
|
|
||||||
return timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setTimeout(Integer timeout) {
|
|
||||||
this.timeout = timeout;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setPassword(String password) {
|
|
||||||
this.password = password;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getPort() {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setPort(Integer port) {
|
|
||||||
this.port = port;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProxyOptions getPreProxyOptions() {
|
|
||||||
return preProxyOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setPreProxyOptions(ProxyOptions preProxyOptions) {
|
|
||||||
this.preProxyOptions = preProxyOptions;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -104,7 +104,7 @@ public enum PanDomainTemplate {
|
|||||||
"lanzoug|" +
|
"lanzoug|" +
|
||||||
"lanzoum" +
|
"lanzoum" +
|
||||||
")\\.com/(.+/)?(?<KEY>.+)"),
|
")\\.com/(.+/)?(?<KEY>.+)"),
|
||||||
"https://lanzoux.com/{shareKey}",
|
"https://w1.lanzn.com/{shareKey}",
|
||||||
LzTool.class),
|
LzTool.class),
|
||||||
|
|
||||||
// https://www.feijix.com/s/
|
// https://www.feijix.com/s/
|
||||||
@@ -263,7 +263,7 @@ public enum PanDomainTemplate {
|
|||||||
|
|
||||||
// https://pan-yz.cldisk.com/external/m/file/953658049102462976
|
// https://pan-yz.cldisk.com/external/m/file/953658049102462976
|
||||||
Pcx("超星云盘(需要referer头)",
|
Pcx("超星云盘(需要referer头)",
|
||||||
compile("https://pan-yz\\.cldisk\\.com/external/m/file/(?<KEY>\\w+)"),
|
compile("https://pan-yz\\.(chaoxing\\.com|cldisk\\.com)/external/m/file/(?<KEY>\\w+)(\\?.*)?"),
|
||||||
"https://pan-yz.cldisk.com/external/m/file/{shareKey}",
|
"https://pan-yz.cldisk.com/external/m/file/{shareKey}",
|
||||||
PcxTool.class),
|
PcxTool.class),
|
||||||
// WPS:分享格式:https://www.kdocs.cn/l/ck0azivLlDi3 ;API格式:https://www.kdocs.cn/api/office/file/{shareKey}/download
|
// WPS:分享格式:https://www.kdocs.cn/l/ck0azivLlDi3 ;API格式:https://www.kdocs.cn/api/office/file/{shareKey}/download
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ import cn.qaiu.entity.FileInfo;
|
|||||||
import cn.qaiu.entity.ShareLinkInfo;
|
import cn.qaiu.entity.ShareLinkInfo;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
import cn.qaiu.util.AESUtils;
|
import cn.qaiu.util.AESUtils;
|
||||||
|
import cn.qaiu.util.AcwScV2Generator;
|
||||||
import cn.qaiu.util.FileSizeConverter;
|
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.Future;
|
||||||
import io.vertx.core.MultiMap;
|
import io.vertx.core.MultiMap;
|
||||||
import io.vertx.core.Promise;
|
import io.vertx.core.Promise;
|
||||||
import io.vertx.core.json.JsonArray;
|
import io.vertx.core.json.JsonArray;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import io.vertx.ext.web.client.WebClientSession;
|
||||||
import io.vertx.uritemplate.UriTemplate;
|
import io.vertx.uritemplate.UriTemplate;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -18,10 +20,12 @@ import java.util.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 蓝奏云优享
|
* 蓝奏云优享
|
||||||
*
|
* v019b22
|
||||||
*/
|
*/
|
||||||
public class IzTool extends PanBase {
|
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 API_URL_PREFIX = "https://api.ilanzou.com/unproved/";
|
||||||
|
|
||||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" +
|
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);
|
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() {
|
public Future<String> parse() {
|
||||||
String shareId = shareLinkInfo.getShareKey();
|
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
|
// 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
|
String url = StringUtils.isBlank(shareLinkInfo.getSharePassword()) ? FIRST_REQUEST_URL
|
||||||
: (FIRST_REQUEST_URL + "&code=" + shareLinkInfo.getSharePassword());
|
: (FIRST_REQUEST_URL + "&code=" + shareLinkInfo.getSharePassword());
|
||||||
client.postAbs(UriTemplate.of(VIP_REQUEST_URL))
|
webClientSession.postAbs(UriTemplate.of(VIP_REQUEST_URL))
|
||||||
.setTemplateParam("uuid", uuid)
|
.setTemplateParam("uuid", uuid)
|
||||||
.setTemplateParam("ts", tsEncode)
|
.setTemplateParam("ts", tsEncode)
|
||||||
.send().onSuccess(r0 -> { // 忽略res
|
.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
|
// 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)
|
.putHeaders(header)
|
||||||
.setTemplateParam("shareId", shareId)
|
.setTemplateParam("shareId", shareId)
|
||||||
.setTemplateParam("uuid", uuid)
|
.setTemplateParam("uuid", uuid)
|
||||||
.setTemplateParam("ts", tsEncode)
|
.setTemplateParam("ts", tsEncode)
|
||||||
.send().onSuccess(res -> {
|
.send().onSuccess(res -> {
|
||||||
JsonObject resJson = asJson(res);
|
String resBody = asText(res);
|
||||||
if (resJson.getInteger("code") != 200) {
|
// 检查是否包含 cookie 验证
|
||||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (resJson.getJsonArray("list").isEmpty()) {
|
handleParseResponse(resBody, shareId);
|
||||||
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));
|
|
||||||
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||||
});
|
});
|
||||||
return promise.future();
|
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
|
@Override
|
||||||
public Future<List<FileInfo>> parseFileList() {
|
public Future<List<FileInfo>> parseFileList() {
|
||||||
Promise<List<FileInfo>> promise = Promise.promise();
|
Promise<List<FileInfo>> promise = Promise.promise();
|
||||||
@@ -174,7 +221,7 @@ public class IzTool extends PanBase {
|
|||||||
log.debug("开始解析目录: {}, shareId: {}, uuid: {}, ts: {}", id, shareId, uuid, tsEncode);
|
log.debug("开始解析目录: {}, shareId: {}, uuid: {}, ts: {}", id, shareId, uuid, tsEncode);
|
||||||
// 开始解析目录: 164312216, shareId: bPMsbg5K, uuid: 0fmVWTx2Ea4zFwkpd7KXf, ts: 20865d7b7f00828279f437cd1f097860
|
// 开始解析目录: 164312216, shareId: bPMsbg5K, uuid: 0fmVWTx2Ea4zFwkpd7KXf, ts: 20865d7b7f00828279f437cd1f097860
|
||||||
// 拿到目录ID
|
// 拿到目录ID
|
||||||
client.postAbs(UriTemplate.of(FILE_LIST_URL))
|
webClientSession.postAbs(UriTemplate.of(FILE_LIST_URL))
|
||||||
.putHeaders(header)
|
.putHeaders(header)
|
||||||
.setTemplateParam("shareId", shareId)
|
.setTemplateParam("shareId", shareId)
|
||||||
.setTemplateParam("uuid", uuid)
|
.setTemplateParam("uuid", uuid)
|
||||||
@@ -264,7 +311,7 @@ public class IzTool extends PanBase {
|
|||||||
public Future<String> parseById() {
|
public Future<String> parseById() {
|
||||||
// 第二次请求
|
// 第二次请求
|
||||||
JsonObject paramJson = (JsonObject)shareLinkInfo.getOtherParam().get("paramJson");
|
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("fidEncode", paramJson.getString("fidEncode"))
|
||||||
.setTemplateParam("uuid", paramJson.getString("uuid"))
|
.setTemplateParam("uuid", paramJson.getString("uuid"))
|
||||||
.setTemplateParam("ts", paramJson.getString("ts"))
|
.setTemplateParam("ts", paramJson.getString("ts"))
|
||||||
|
|||||||
@@ -1,30 +1,72 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
|
import cn.qaiu.entity.FileInfo;
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
import cn.qaiu.entity.ShareLinkInfo;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
|
import cn.qaiu.util.FileSizeConverter;
|
||||||
import io.vertx.core.Future;
|
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.JsonArray;
|
||||||
import io.vertx.core.json.JsonObject;
|
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;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href="https://lecloud.lenovo.com/">联想乐云</a>
|
* <a href="https://lecloud.lenovo.com/">联想乐云</a>
|
||||||
*/
|
*/
|
||||||
public class LeTool extends PanBase {
|
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) {
|
public LeTool(ShareLinkInfo shareLinkInfo) {
|
||||||
super(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() {
|
public Future<String> parse() {
|
||||||
final String dataKey = shareLinkInfo.getShareKey();
|
final String dataKey = getCleanShareId();
|
||||||
final String pwd = shareLinkInfo.getSharePassword();
|
final String pwd = shareLinkInfo.getSharePassword();
|
||||||
// {"shareId":"xxx","password":"xxx","directoryId":"-1"}
|
// {"shareId":"xxx","password":"xxx","directoryId":"-1"}
|
||||||
String apiUrl1 = API_URL_PREFIX + "shareInfo";
|
String apiUrl1 = API_URL_PREFIX + "shareInfo";
|
||||||
client.postAbs(apiUrl1)
|
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 -> {
|
.onSuccess(res -> {
|
||||||
JsonObject resJson = asJson(res);
|
JsonObject resJson = asJson(res);
|
||||||
if (resJson.containsKey("result")) {
|
if (resJson.containsKey("result")) {
|
||||||
@@ -44,7 +86,19 @@ public class LeTool extends PanBase {
|
|||||||
}
|
}
|
||||||
JsonObject fileInfoJson = files.getJsonObject(0);
|
JsonObject fileInfoJson = files.getJsonObject(0);
|
||||||
if (fileInfoJson != null) {
|
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");
|
String fileId = fileInfoJson.getString("fileId");
|
||||||
// 根据文件ID获取跳转链接
|
// 根据文件ID获取跳转链接
|
||||||
getDownURL(dataKey, fileId);
|
getDownURL(dataKey, fileId);
|
||||||
@@ -59,13 +113,205 @@ public class LeTool extends PanBase {
|
|||||||
return promise.future();
|
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) {
|
private void getDownURL(String key, String fileId) {
|
||||||
String uuid = UUID.randomUUID().toString();
|
String uuid = UUID.randomUUID().toString();
|
||||||
JsonArray fileIds = JsonArray.of(fileId);
|
JsonArray fileIds = JsonArray.of(fileId);
|
||||||
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
|
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
|
||||||
// {"fileIds":[123],"shareId":"xxx","browserId":"uuid"}
|
// {"fileIds":[123],"shareId":"xxx","browserId":"uuid"}
|
||||||
client.postAbs(apiUrl2)
|
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 -> {
|
.onSuccess(res -> {
|
||||||
JsonObject resJson = asJson(res);
|
JsonObject resJson = asJson(res);
|
||||||
if (resJson.containsKey("result")) {
|
if (resJson.containsKey("result")) {
|
||||||
@@ -89,4 +335,51 @@ public class LeTool extends PanBase {
|
|||||||
}
|
}
|
||||||
}).onFailure(handleFail(apiUrl2));
|
}).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.core.json.JsonObject;
|
||||||
import io.vertx.ext.web.client.WebClient;
|
import io.vertx.ext.web.client.WebClient;
|
||||||
import io.vertx.ext.web.client.WebClientSession;
|
import io.vertx.ext.web.client.WebClientSession;
|
||||||
import org.apache.commons.lang3.RegExUtils;
|
|
||||||
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
|
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
|
||||||
|
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -29,13 +27,14 @@ import java.util.regex.Pattern;
|
|||||||
*/
|
*/
|
||||||
public class LzTool extends PanBase {
|
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("""
|
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: 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-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
|
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
|
Cache-Control: max-age=0
|
||||||
Cookie: codelen=1; pc_ad1=1
|
|
||||||
DNT: 1
|
DNT: 1
|
||||||
Priority: u=0, i
|
Priority: u=0, i
|
||||||
Sec-CH-UA: "Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"
|
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)
|
.putHeaders(headers0)
|
||||||
.send().onSuccess(res -> {
|
.send().onSuccess(res -> {
|
||||||
String html = asText(res);
|
String html = asText(res);
|
||||||
try {
|
if (html.contains("var arg1='")) {
|
||||||
setFileInfo(html, shareLinkInfo);
|
webClientSession = WebClientSession.create(clientNoRedirects);
|
||||||
} catch (Exception e) {
|
setCookie(html);
|
||||||
e.printStackTrace();
|
webClientSession.getAbs(sUrl)
|
||||||
}
|
.putHeaders(headers0)
|
||||||
// 匹配iframe
|
.send().onSuccess(res2 -> {
|
||||||
Pattern compile = Pattern.compile("src=\"(/fn\\?[a-zA-Z\\d_+/=]{16,})\"");
|
String html2 = asText(res2);
|
||||||
Matcher matcher = compile.matcher(html);
|
doParser(html2, pwd, sUrl);
|
||||||
// 没有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();
|
|
||||||
|
|
||||||
// 去TMD正则
|
} else {
|
||||||
// Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
|
doParser(html, pwd, sUrl);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}).onFailure(handleFail(sUrl));
|
}).onFailure(handleFail(sUrl));
|
||||||
return promise.future();
|
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) {
|
private String getJsByPwd(String pwd, String html, String subText) {
|
||||||
String jsText = getJsText(html);
|
String jsText = getJsText(html);
|
||||||
|
|
||||||
if (jsText == null) {
|
if (jsText == null) {
|
||||||
throw new RuntimeException("js脚本匹配失败, 可能分享已失效");
|
throw new RuntimeException("获取失败1, 可能分享已失效");
|
||||||
}
|
}
|
||||||
jsText = jsText.replace("document.getElementById('pwd').value", "\"" + pwd + "\"");
|
jsText = jsText.replace("document.getElementById('pwd').value", "\"" + pwd + "\"");
|
||||||
int i = jsText.indexOf(subText);
|
int i = jsText.indexOf(subText);
|
||||||
@@ -131,7 +177,7 @@ public class LzTool extends PanBase {
|
|||||||
return html.substring(startPos, endPos).replaceAll("<!--.*-->", "");
|
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) {
|
if (obj == null) {
|
||||||
fail("需要访问密码");
|
fail("需要访问密码");
|
||||||
return;
|
return;
|
||||||
@@ -163,7 +209,7 @@ public class LzTool extends PanBase {
|
|||||||
headers.set("referer", key);
|
headers.set("referer", key);
|
||||||
// action=downprocess&signs=%3Fctdf&websignkey=I5gl&sign=BWMGOF1sBTRWXwI9BjZdYVA7BDhfNAIyUG9UawJtUGMIPlAhACkCa1UyUTAAYFxvUj5XY1E7UGFXaFVq&websign=&kd=1&ves=1
|
// action=downprocess&signs=%3Fctdf&websignkey=I5gl&sign=BWMGOF1sBTRWXwI9BjZdYVA7BDhfNAIyUG9UawJtUGMIPlAhACkCa1UyUTAAYFxvUj5XY1E7UGFXaFVq&websign=&kd=1&ves=1
|
||||||
String url = SHARE_URL_PREFIX + url0;
|
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 {
|
try {
|
||||||
JsonObject urlJson = asJson(res2);
|
JsonObject urlJson = asJson(res2);
|
||||||
String name = urlJson.getString("inf");
|
String name = urlJson.getString("inf");
|
||||||
@@ -178,7 +224,6 @@ public class LzTool extends PanBase {
|
|||||||
|
|
||||||
String downUrl = urlJson.getString("dom") + "/file/" + urlJson.getString("url");
|
String downUrl = urlJson.getString("dom") + "/file/" + urlJson.getString("url");
|
||||||
headers.remove("Referer");
|
headers.remove("Referer");
|
||||||
WebClientSession webClientSession = WebClientSession.create(client);
|
|
||||||
webClientSession.getAbs(downUrl).putHeaders(headers).send()
|
webClientSession.getAbs(downUrl).putHeaders(headers).send()
|
||||||
.onSuccess(res3 -> {
|
.onSuccess(res3 -> {
|
||||||
String location = res3.headers().get("Location");
|
String location = res3.headers().get("Location");
|
||||||
@@ -195,12 +240,13 @@ public class LzTool extends PanBase {
|
|||||||
nettyCookie.setPath("/"); // 设置路径
|
nettyCookie.setPath("/"); // 设置路径
|
||||||
nettyCookie.setSecure(false);
|
nettyCookie.setSecure(false);
|
||||||
nettyCookie.setHttpOnly(false);
|
nettyCookie.setHttpOnly(false);
|
||||||
webClientSession.cookieStore().put(nettyCookie);
|
WebClientSession webClientSession2 = WebClientSession.create(clientNoRedirects);
|
||||||
webClientSession.getAbs(downUrl).putHeaders(headers).send()
|
webClientSession2.cookieStore().put(nettyCookie);
|
||||||
|
webClientSession2.getAbs(downUrl).putHeaders(headers).send()
|
||||||
.onSuccess(res4 -> {
|
.onSuccess(res4 -> {
|
||||||
String location0 = res4.headers().get("Location");
|
String location0 = res4.headers().get("Location");
|
||||||
if (location0 == null) {
|
if (location0 == null) {
|
||||||
fail(downUrl + " -> 直链获取失败, 可能分享已失效");
|
fail(downUrl + " -> 直链获取失败2, 可能分享已失效");
|
||||||
} else {
|
} else {
|
||||||
setDateAndComplate(location0);
|
setDateAndComplate(location0);
|
||||||
}
|
}
|
||||||
@@ -248,67 +294,98 @@ public class LzTool extends PanBase {
|
|||||||
String sUrl = shareLinkInfo.getShareUrl();
|
String sUrl = shareLinkInfo.getShareUrl();
|
||||||
String pwd = shareLinkInfo.getSharePassword();
|
String pwd = shareLinkInfo.getSharePassword();
|
||||||
|
|
||||||
WebClient client = clientNoRedirects;
|
webClientSession.getAbs(sUrl).send().onSuccess(res -> {
|
||||||
client.getAbs(sUrl).send().onSuccess(res -> {
|
|
||||||
String html = res.bodyAsString();
|
String html = res.bodyAsString();
|
||||||
try {
|
// 检查是否需要 cookie 验证
|
||||||
String jsText = getJsByPwd(pwd, html, "var urls =window.location.href");
|
if (html.contains("var arg1='")) {
|
||||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "file");
|
webClientSession = WebClientSession.create(clientNoRedirects);
|
||||||
Map<String, Object> data = CastUtil.cast(scriptObjectMirror.get("data"));
|
setCookie(html);
|
||||||
MultiMap map = MultiMap.caseInsensitiveMultiMap();
|
// 重新请求
|
||||||
data.forEach((k, v) -> map.set(k, v.toString()));
|
webClientSession.getAbs(sUrl).send().onSuccess(res2 -> {
|
||||||
log.debug("解析参数: {}", map);
|
handleFileListParse(res2.bodyAsString(), pwd, sUrl, promise);
|
||||||
MultiMap headers = getHeaders(sUrl);
|
}).onFailure(err -> promise.fail(err));
|
||||||
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
handleFileListParse(html, pwd, sUrl, promise);
|
||||||
|
}).onFailure(err -> promise.fail(err));
|
||||||
return promise.future();
|
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) {
|
void setFileInfo(String html, ShareLinkInfo shareLinkInfo) {
|
||||||
// 写入 fileInfo
|
// 写入 fileInfo
|
||||||
FileInfo fileInfo = new FileInfo();
|
FileInfo fileInfo = new FileInfo();
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
|
import cn.qaiu.entity.FileInfo;
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
import cn.qaiu.entity.ShareLinkInfo;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
|
import cn.qaiu.util.FileSizeConverter;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.MultiMap;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
import java.util.regex.Matcher;
|
||||||
import io.vertx.uritemplate.UriTemplate;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href="https://passport2.chaoxing.com">超星云盘</a>
|
* <a href="https://passport2.chaoxing.com">超星云盘</a>
|
||||||
@@ -19,24 +21,135 @@ public class PcxTool extends PanBase {
|
|||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
client.getAbs(shareLinkInfo.getShareUrl())
|
client.getAbs(shareLinkInfo.getShareUrl())
|
||||||
.send().onSuccess(res -> {
|
.send().onSuccess(res -> {
|
||||||
// 'download': 'https://d0.ananas.chaoxing.com/download/de08dcf546e4dd88a17bead86ff6338d?at_=1740211698795&ak_=d62a3acbd5ce43e1e8565b67990691e4&ad_=8c4ef22e980ee0dd9532ec3757ab19f8&fn=33.c'
|
|
||||||
String body = res.bodyAsString();
|
String body = res.bodyAsString();
|
||||||
// 获取download
|
try {
|
||||||
String str = "var fileinfo = {";
|
// 提取文件信息
|
||||||
String fileInfo = res.bodyAsString().substring(res.bodyAsString().indexOf(str) + str.length() - 1
|
setFileInfo(body);
|
||||||
, res.bodyAsString().indexOf("};") + 1);
|
|
||||||
fileInfo = fileInfo.replace("'", "\"");
|
// 直接用正则提取download链接
|
||||||
JsonObject jsonObject = new JsonObject(fileInfo);
|
String download = extractDownloadUrl(body);
|
||||||
String download = jsonObject.getString("download");
|
if (download != null && download.contains("fn=")) {
|
||||||
if (download.contains("fn=")) {
|
complete(download);
|
||||||
complete(download);
|
} else {
|
||||||
} else {
|
fail("获取下载链接失败");
|
||||||
fail("获取下载链接失败: 不支持的文件类型: {}", jsonObject.getString("suffix"));
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("解析文件信息失败: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
}).onFailure(handleFail(shareLinkInfo.getShareUrl()));
|
}).onFailure(handleFail(shareLinkInfo.getShareUrl()));
|
||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从HTML中提取download链接
|
||||||
|
*/
|
||||||
|
private String extractDownloadUrl(String html) {
|
||||||
|
// 匹配 'download': 'https://xxx' 或 "download": "https://xxx"
|
||||||
|
Pattern pattern = Pattern.compile("['\"]download['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
Matcher matcher = pattern.matcher(html);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从HTML中提取文件信息并设置到shareLinkInfo
|
||||||
|
*/
|
||||||
|
private void setFileInfo(String html) {
|
||||||
|
try {
|
||||||
|
FileInfo fileInfo = new FileInfo();
|
||||||
|
|
||||||
|
// 提取文件名:从<title>标签或文件名input
|
||||||
|
String fileName = extractByRegex(html, "<title>([^<]+)</title>");
|
||||||
|
if (fileName == null) {
|
||||||
|
fileName = extractByRegex(html, "<input id=\"filename\" type=\"hidden\" value=\"([^\"]+)\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取文件大小:'filesize': 'xxx' 或 "filesize": "xxx"
|
||||||
|
String fileSizeStr = extractByRegex(html, "['\"]filesize['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
Long fileSize = null;
|
||||||
|
if (fileSizeStr != null) {
|
||||||
|
try {
|
||||||
|
fileSize = Long.parseLong(fileSizeStr);
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取文件类型/后缀:'suffix': 'xxx' 或 "suffix": "xxx"
|
||||||
|
String suffix = extractByRegex(html, "['\"]suffix['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
|
||||||
|
// 提取objectId(文件ID):'objectId': 'xxx' 或 "objectId": "xxx"
|
||||||
|
String objectId = extractByRegex(html, "['\"]objectId['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
|
||||||
|
// 提取创建者:'creator': 'xxx' 或 "creator": "xxx"
|
||||||
|
String creator = extractByRegex(html, "['\"]creator['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
|
||||||
|
// 提取上传时间:'uploadDate': timestamp
|
||||||
|
String uploadDate = extractByRegex(html, "['\"]uploadDate['\"]\\s*:\\s*(\\d+)");
|
||||||
|
|
||||||
|
// 提取缩略图:'thumbnail': 'xxx' 或 "thumbnail": "xxx"
|
||||||
|
String thumbnail = extractByRegex(html, "['\"]thumbnail['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
|
||||||
|
// 设置文件信息
|
||||||
|
if (fileName != null) {
|
||||||
|
fileInfo.setFileName(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize != null) {
|
||||||
|
fileInfo.setSize(fileSize);
|
||||||
|
fileInfo.setSizeStr(FileSizeConverter.convertToReadableSize(fileSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suffix != null) {
|
||||||
|
fileInfo.setFileType(suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectId != null) {
|
||||||
|
fileInfo.setFileId(objectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creator != null) {
|
||||||
|
fileInfo.setCreateBy(creator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadDate != null) {
|
||||||
|
try {
|
||||||
|
long timestamp = Long.parseLong(uploadDate);
|
||||||
|
// 转换为日期格式
|
||||||
|
java.time.Instant instant = java.time.Instant.ofEpochMilli(timestamp);
|
||||||
|
java.time.LocalDateTime dateTime = java.time.LocalDateTime.ofInstant(instant,
|
||||||
|
java.time.ZoneId.systemDefault());
|
||||||
|
fileInfo.setCreateTime(dateTime.format(
|
||||||
|
java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thumbnail != null) {
|
||||||
|
fileInfo.setPreviewUrl(thumbnail);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInfo.setPanType(shareLinkInfo.getType());
|
||||||
|
|
||||||
|
// 将文件信息存储到shareLinkInfo的otherParam中
|
||||||
|
shareLinkInfo.getOtherParam().put("fileInfo", fileInfo);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("提取文件信息失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用正则表达式提取内容
|
||||||
|
*/
|
||||||
|
private String extractByRegex(String text, String regex) {
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
Matcher matcher = pattern.matcher(text);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// public static void main(String[] args) {
|
// public static void main(String[] args) {
|
||||||
// String s = new PcxTool(ShareLinkInfo.newBuilder().shareUrl("https://pan-yz.cldisk.com/external/m/file/953658049102462976")
|
// String s = new PcxTool(ShareLinkInfo.newBuilder().shareUrl("https://pan-yz.cldisk.com/external/m/file/953658049102462976")
|
||||||
|
|||||||
@@ -25,14 +25,16 @@
|
|||||||
|
|
||||||
<packageDirectory>${project.basedir}/web-service/target/package</packageDirectory>
|
<packageDirectory>${project.basedir}/web-service/target/package</packageDirectory>
|
||||||
|
|
||||||
<vertx.version>4.5.22</vertx.version>
|
<!-- Vert.x 4.5.24 已包含安全修复,无需单独指定 Netty 版本 -->
|
||||||
|
<vertx.version>4.5.14</vertx.version>
|
||||||
<org.reflections.version>0.10.2</org.reflections.version>
|
<org.reflections.version>0.10.2</org.reflections.version>
|
||||||
<lombok.version>1.18.38</lombok.version>
|
<lombok.version>1.18.38</lombok.version>
|
||||||
<slf4j.version>2.0.5</slf4j.version>
|
<slf4j.version>2.0.16</slf4j.version>
|
||||||
<commons-lang3.version>3.18.0</commons-lang3.version>
|
<commons-lang3.version>3.18.0</commons-lang3.version>
|
||||||
<commons-beanutils2.version>2.0.0</commons-beanutils2.version>
|
<commons-beanutils2.version>2.0.0</commons-beanutils2.version>
|
||||||
<jackson.version>2.14.2</jackson.version>
|
<jackson.version>2.18.2</jackson.version>
|
||||||
<logback.version>1.5.19</logback.version>
|
<!-- Logback 最新稳定版 -->
|
||||||
|
<logback.version>1.5.18</logback.version>
|
||||||
<junit.version>4.13.2</junit.version>
|
<junit.version>4.13.2</junit.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -46,6 +48,18 @@
|
|||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 统一管理 logback 版本 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-core</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.qaiu</groupId>
|
<groupId>cn.qaiu</groupId>
|
||||||
<artifactId>core</artifactId>
|
<artifactId>core</artifactId>
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ export default {
|
|||||||
const data = result.data
|
const data = result.data
|
||||||
|
|
||||||
// 检查是否支持目录解析
|
// 检查是否支持目录解析
|
||||||
const supportedPans = ["iz", "lz", "fj", "ye"]
|
const supportedPans = ["iz", "lz", "fj", "ye", "le"]
|
||||||
if (!supportedPans.includes(data.shareLinkInfo.type)) {
|
if (!supportedPans.includes(data.shareLinkInfo.type)) {
|
||||||
this.$message.error("当前网盘不支持目录解析")
|
this.$message.error("当前网盘不支持目录解析")
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user