mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 12:23:03 +00:00
Compare commits
210 Commits
0.1.8.bate
...
0.1.7-rele
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
068efd0503 | ||
|
|
d6de9851df | ||
|
|
a9a048a9ce | ||
|
|
481e7b1240 | ||
|
|
e406e03dc2 | ||
|
|
acbf56adbc | ||
|
|
1cf82b8df9 | ||
|
|
a8405830f2 | ||
|
|
2673f23e64 | ||
|
|
cbaf6e9ac0 | ||
|
|
edb2d65638 | ||
|
|
f8f64fdcb1 | ||
|
|
ae86b85e51 | ||
|
|
c35bc22d02 | ||
|
|
a682d2d876 | ||
|
|
72f363d0e6 | ||
|
|
4b84ecd5fc | ||
|
|
632945d39a | ||
|
|
40bbd3f135 | ||
|
|
d822090c76 | ||
|
|
bd2ab30e7a | ||
|
|
f306170994 | ||
|
|
c8e12de768 | ||
|
|
d602c8a020 | ||
|
|
efe39eef8d | ||
|
|
d828c7ac8b | ||
|
|
37babc9a89 | ||
|
|
8be497a290 | ||
|
|
9ff1ea2ff2 | ||
|
|
fc64363b47 | ||
|
|
36ab2985b1 | ||
|
|
8ba95932ba | ||
|
|
b8aaa59437 | ||
|
|
1d6953d7c7 | ||
|
|
98c336ab84 | ||
|
|
d5e2b9cb3e | ||
|
|
7c4a243429 | ||
|
|
64e312f108 | ||
|
|
265d11e74c | ||
|
|
fcc0f71c23 | ||
|
|
4fd4bfb048 | ||
|
|
da07cfaaff | ||
|
|
7e31adcad9 | ||
|
|
378ae84816 | ||
|
|
a32abe8bfa | ||
|
|
f7ecf3e66c | ||
|
|
e9e503c685 | ||
|
|
85c697ea92 | ||
|
|
2018b20be5 | ||
|
|
e1c61fb51a | ||
|
|
cdeb63e225 | ||
|
|
7b6a79f83a | ||
|
|
dfd7e7a934 | ||
|
|
8deb932685 | ||
|
|
da063461be | ||
|
|
3234f643d5 | ||
|
|
332cc587a2 | ||
|
|
b30d01e154 | ||
|
|
79d8aa0868 | ||
|
|
a724d703ed | ||
|
|
18478b2a0b | ||
|
|
dde54ed9cc | ||
|
|
3c56a7183e | ||
|
|
38a3ae8a56 | ||
|
|
00b30331eb | ||
|
|
8397c24715 | ||
|
|
3ee113e81f | ||
|
|
cde641c8db | ||
|
|
30cd2f5850 | ||
|
|
71066ded98 | ||
|
|
8441954075 | ||
|
|
5e0568fa0e | ||
|
|
594e9af71d | ||
|
|
9bc7e9aa26 | ||
|
|
5805edf0a9 | ||
|
|
9614a36fc2 | ||
|
|
f45031c3f1 | ||
|
|
28e0542c8e | ||
|
|
b78a8300c6 | ||
|
|
ca06d5ae98 | ||
|
|
0c5e53b86e | ||
|
|
f45e6f3468 | ||
|
|
08ffec524e | ||
|
|
62dea67d69 | ||
|
|
8108a4dd3f | ||
|
|
56334fa4a2 | ||
|
|
093b337396 | ||
|
|
4bedf4486b | ||
|
|
b700493a6c | ||
|
|
44b1168bb5 | ||
|
|
a351364faf | ||
|
|
6bd075e17c | ||
|
|
1e77b20e44 | ||
|
|
0c2165dbf3 | ||
|
|
7595345e7b | ||
|
|
d13fe1fc21 | ||
|
|
f0269b1687 | ||
|
|
257147dd75 | ||
|
|
6fe824e705 | ||
|
|
f482982a25 | ||
|
|
e3ec81bd9b | ||
|
|
4771a5b5ce | ||
|
|
1292440f95 | ||
|
|
c2052ddbd8 | ||
|
|
f65dfacf4b | ||
|
|
27c2c1ab29 | ||
|
|
b4471dd9c3 | ||
|
|
69d8b645a4 | ||
|
|
67dc452819 | ||
|
|
60df94ce86 | ||
|
|
d6575d5b81 | ||
|
|
2ffa31f655 | ||
|
|
1ae2c89d4f | ||
|
|
2de5790b2a | ||
|
|
e5b892c9d1 | ||
|
|
1ab5059a41 | ||
|
|
1d10e7ab50 | ||
|
|
0477cc4815 | ||
|
|
9d3fce3dee | ||
|
|
e07d427f85 | ||
|
|
998c6e4627 | ||
|
|
e284dfca63 | ||
|
|
cccacde288 | ||
|
|
ee99599d4f | ||
|
|
574d5861f5 | ||
|
|
c3173b7f93 | ||
|
|
6dc647d0be | ||
|
|
ba5ad53529 | ||
|
|
3c5a0d9c9d | ||
|
|
bb1c296872 | ||
|
|
b36e3628bd | ||
|
|
837a265fd8 | ||
|
|
208c430875 | ||
|
|
639f8df534 | ||
|
|
4f5e23ebb6 | ||
|
|
d86c67314c | ||
|
|
d1ba790d05 | ||
|
|
63b1cad839 | ||
|
|
610c6c8d2a | ||
|
|
7f50ee5217 | ||
|
|
5883c7c174 | ||
|
|
0b73738e39 | ||
|
|
4e11b61fdb | ||
|
|
853246d363 | ||
|
|
e1d880318a | ||
|
|
533f6f8596 | ||
|
|
68b7c57384 | ||
|
|
089dc5713b | ||
|
|
2596e58943 | ||
|
|
7d8b39ad92 | ||
|
|
9419c7e0ca | ||
|
|
f492e3c031 | ||
|
|
8973a0e4b2 | ||
|
|
fff8cfe460 | ||
|
|
3b3dd0634f | ||
|
|
e2945b8f34 | ||
|
|
5343c68603 | ||
|
|
daadb6e9cc | ||
|
|
b8057135a9 | ||
|
|
e9ed8715af | ||
|
|
e941d76507 | ||
|
|
f2280c8e86 | ||
|
|
b189614d29 | ||
|
|
ea9d1067ed | ||
|
|
70fe0c23ae | ||
|
|
03c4c8c581 | ||
|
|
9a3baebec9 | ||
|
|
3574b58d21 | ||
|
|
74e95d0f12 | ||
|
|
a14db13485 | ||
|
|
37f5f2a30d | ||
|
|
e7ebb7c8c3 | ||
|
|
11decadb4c | ||
|
|
34c6d26581 | ||
|
|
2155c3e0c0 | ||
|
|
0bc23e76bf | ||
|
|
095040efce | ||
|
|
d98751b0e3 | ||
|
|
3e31e97c35 | ||
|
|
eebce94e88 | ||
|
|
2ca7a78920 | ||
|
|
2c243650f6 | ||
|
|
a257cd2f69 | ||
|
|
d0d5c96b9e | ||
|
|
00af483239 | ||
|
|
25534452ae | ||
|
|
804266d5af | ||
|
|
7fa2ea59a5 | ||
|
|
c9e714418e | ||
|
|
b3beb61337 | ||
|
|
2270a05631 | ||
|
|
5fb3d2a2d6 | ||
|
|
34f2670700 | ||
|
|
d4586b342e | ||
|
|
1651f1ea93 | ||
|
|
11a054dc2c | ||
|
|
fc233bcea3 | ||
|
|
e7e3888b13 | ||
|
|
91845cc08c | ||
|
|
94cde1e953 | ||
|
|
678866f654 | ||
|
|
f7ccc3c3e3 | ||
|
|
1799763f7f | ||
|
|
5ba38f4f1d | ||
|
|
07d8ed19b0 | ||
|
|
15b6468333 | ||
|
|
2457ded158 | ||
|
|
54a1a6b7f4 | ||
|
|
167149f410 | ||
|
|
2c7a17c3b0 |
@@ -1,28 +0,0 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/java
|
||||
{
|
||||
"name": "Java",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/java:0-17",
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/java:1": {
|
||||
"version": "none",
|
||||
"installMaven": "true",
|
||||
"installGradle": "false"
|
||||
},
|
||||
"ghcr.io/devcontainers-contrib/features/ant-sdkman:2": {}
|
||||
}
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "java -version",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
2
.github/workflows/maven.yml
vendored
2
.github/workflows/maven.yml
vendored
@@ -37,8 +37,6 @@ jobs:
|
||||
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
|
||||
- name: Update dependency graph
|
||||
uses: advanced-security/maven-dependency-submission-action@v3
|
||||
with:
|
||||
ignore-maven-wrapper: true
|
||||
|
||||
# - uses: release-drafter/release-drafter@v5
|
||||
# env:
|
||||
|
||||
117
.mvn/wrapper/MavenWrapperDownloader.java
vendored
117
.mvn/wrapper/MavenWrapperDownloader.java
vendored
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
private static final String WRAPPER_VERSION = "0.5.6";
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
|
||||
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
* use instead of the default one.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||
".mvn/wrapper/maven-wrapper.properties";
|
||||
|
||||
/**
|
||||
* Path where the maven-wrapper.jar will be saved to.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||
".mvn/wrapper/maven-wrapper.jar";
|
||||
|
||||
/**
|
||||
* Name of the property which should be used to override the default download url for the wrapper.
|
||||
*/
|
||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("- Downloader started");
|
||||
File baseDirectory = new File(args[0]);
|
||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||
|
||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||
// wrapperUrl parameter.
|
||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||
String url = DEFAULT_DOWNLOAD_URL;
|
||||
if(mavenWrapperPropertyFile.exists()) {
|
||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||
try {
|
||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||
Properties mavenWrapperProperties = new Properties();
|
||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||
} catch (IOException e) {
|
||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||
} finally {
|
||||
try {
|
||||
if(mavenWrapperPropertyFileInputStream != null) {
|
||||
mavenWrapperPropertyFileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore ...
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if(!outputFile.getParentFile().exists()) {
|
||||
if(!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
try {
|
||||
downloadFileFromURL(url, outputFile);
|
||||
System.out.println("Done");
|
||||
System.exit(0);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("- Error downloading");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
||||
String username = System.getenv("MVNW_USERNAME");
|
||||
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
||||
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Binary file not shown.
2
.mvn/wrapper/maven-wrapper.properties
vendored
2
.mvn/wrapper/maven-wrapper.properties
vendored
@@ -1,2 +0,0 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||
70
.vscode/launch.json
vendored
70
.vscode/launch.json
vendored
@@ -1,70 +0,0 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Current File",
|
||||
"request": "launch",
|
||||
"mainClass": "${file}"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "StringCase",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.vx.core.util.StringCase",
|
||||
"projectName": "core"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "FCURLParser",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.parser.FCURLParser",
|
||||
"projectName": "parser"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "QkTool",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.parser.impl.QkTool",
|
||||
"projectName": "parser"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "WebClientExample",
|
||||
"request": "launch",
|
||||
"mainClass": "qaiu.web.test.WebClientExample",
|
||||
"projectName": "parser"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "AppMain",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.lz.AppMain",
|
||||
"projectName": "web-service"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "TestJs",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.web.test.TestJs",
|
||||
"projectName": "web-service"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "TestOS",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.web.test.TestOS",
|
||||
"projectName": "web-service"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "WebProxyExamples",
|
||||
"request": "launch",
|
||||
"mainClass": "cn.qaiu.web.test.WebProxyExamples",
|
||||
"projectName": "web-service"
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,4 +1,3 @@
|
||||
{
|
||||
"java.compile.nullAnalysis.mode": "automatic",
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
"java.compile.nullAnalysis.mode": "automatic"
|
||||
}
|
||||
118
README.md
118
README.md
@@ -1,6 +1,5 @@
|
||||
云盘解析服务 (nfd云解析)
|
||||
预览地址 https://lz.qaiu.top
|
||||
main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/netdisk-fast-download/tree/main-jdk11)
|
||||
**注意: 请不要过度依赖lz.qaiu.top预览地址服务,建议本地搭建或者云服务器自行搭建。
|
||||
解析次数过多IP会被部分网盘厂商限制,不推荐做公共解析。**
|
||||
|
||||
@@ -13,7 +12,7 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
|
||||
网盘直链解析工具能把网盘分享下载链接转化为直链,已支持蓝奏云/蓝奏云优享/奶牛快传/移动云云空间/小飞机盘/亿方云/123云盘/Cloudreve等,支持加密分享。
|
||||
|
||||
|
||||
*重要声明:本项目仅供学习参考;请不要将此项目用于任何商业用途,否则可能带来严重的后果。转发/分享该项目请注明来源*
|
||||
*重要声明:本项目仅供学习参考;请不要将此项目用于任何商业用途,否则可能带来严重的后果。*
|
||||
|
||||
## 网盘支持情况:
|
||||
> 20230905 奶牛云直链做了防盗链,需加入请求头:Referer: https://cowtransfer.com/
|
||||
@@ -23,59 +22,87 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
|
||||
`网盘名称(网盘标识):`
|
||||
|
||||
- [蓝奏云 (lz)](https://pc.woozooo.com/)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [蓝奏云优享 (iz)](https://www.ilanzou.com/)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [奶牛快传 (cow)](https://cowtransfer.com/)
|
||||
- [移动云云空间 (ec)](https://www.ecpan.cn/web)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [移动云空间 (ec)](https://www.ecpan.cn/web)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [小飞机网盘 (fj)](https://www.feijipan.com/)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [亿方云 (fc)](https://www.fangcloud.com/)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [123云盘 (ye)](https://www.123pan.com/)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [文叔叔 (ws)](https://www.wenshushu.cn/)
|
||||
- [联想乐云 (le)](https://lecloud.lenovo.com/)
|
||||
- [QQ邮箱文件中转站 (qq)](https://mail.qq.com/)
|
||||
- [超星网盘-开发中 (cx)](https://passport2.chaoxing.com/login?newversion=true&refer=https%3A%2F%2Fpan-yz.chaoxing.com%2F)
|
||||
- [Cloudreve自建网盘--正在优化 (ce)](https://github.com/cloudreve/Cloudreve)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [Cloudreve自建网盘 (ce)](https://github.com/cloudreve/Cloudreve)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [QQ邮箱 (qq) 暂不可用-存在cookie问题](https://wx.mail.qq.com/)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析(用户无法直接使用直链)
|
||||
- [夸克网盘 (qk) 寄了](https://pan.quark.cn/)
|
||||
- [UC网盘 (uc) 寄了](https://fast.uc.cn/)
|
||||
|
||||
**TODO:**
|
||||
- 登录接口, 文件上传/下载/分享后端接口
|
||||
- 短地址服务
|
||||
- 前端界面(建设中...)
|
||||
|
||||
### API接口说明
|
||||
your_host指的是您的域名或者IP,实际使用时替换为实际域名或者IP,端口默认6400,可以使用nginx代理来做域名访问。
|
||||
解析方式分为两种类型直接跳转下载文件和获取下载链接,每一种都提供了两种接口形式parser和网盘标志/分享key拼接的短地址(标志短链),所有规则参考示例。
|
||||
your_host指的是您的域名或者IP,实际使用时替换为实际域名或者IP。
|
||||
解析方式分为两种类型直接跳转下载链接和获取下载链接(JSON),每一种都提供了两种接口形式parser和网盘标志/分享key拼接的短地址(标志短链),所有规则参考示例。
|
||||
- 通用接口: `/parser?url=分享链接`,加密分享需要加上参数pwd=密码;
|
||||
- 标志短链: `/网盘标识/分享key` 在分享Key后面加上@密码;
|
||||
- 直链JSON: `通用接口`和`标志短链`前加上`/json` 加密分享的密码规则同上;
|
||||
- 网盘标识参考上面网盘支持情况
|
||||
- 当带有分享密码时需要加上密码参数(pwd)
|
||||
- 移动云云空间,小飞机网盘的加密分享的密码可以忽略
|
||||
- 移动云空间分享key取分享链接中的data参数,比如`&data=xxx`的参数就是xxx
|
||||
- 括号内是可选内容: 表示当带有分享密码时需要加上密码参数
|
||||
- 移动云空间,小飞机网盘的加密分享的密码可以忽略
|
||||
|
||||
API规则:
|
||||
规则示例:
|
||||
```
|
||||
|
||||
1. 解析并自动302跳转 :
|
||||
http://your_host/parser?url=分享链接&pwd=xxx
|
||||
http://your_host/网盘标识/分享key@分享密码
|
||||
http://your_host/parser?url=分享链接(&pwd=xxx)
|
||||
http://your_host/网盘标识/分享key(@分享密码)
|
||||
2. 获取解析后的直链--JSON格式
|
||||
http://your_host/json/parser?url=分享链接&pwd=xxx
|
||||
http://your_host/json/网盘标识/分享key@分享密码
|
||||
http://your_host/json/parser?url=分享链接(&pwd=xxx)
|
||||
http://your_host/json/网盘标识/分享key(@分享密码)
|
||||
3. 需要特殊处理的网盘分享:
|
||||
1. 移动云空间(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
|
||||
|
||||
2. Cloudreve自建网盘解析规则:
|
||||
1. 标志短链: 根据网盘使用https和http选择 http://your_host/ce/https_网盘域名_s_wDz5TK 或 http://your_host/ce/http_网盘域名_s_wDz5TK
|
||||
网盘域名指的是Cloudreve搭建网盘的主域名比如pan.huang1111.cn,如果存在子路径需要将/替换为_,是否存在子路径看分享链接格式是否是://网盘域名/子路径/s/xxx,一般不存在子路径:网盘域名/s/xxx,
|
||||
比如: http://127.0.0.1:6400/ce/https_pan.huang1111.cn_s_wDz5TK
|
||||
2. parser接口 -> http://your_host/parser?url=分享链接(&pwd=xxx)
|
||||
比如: http://127.0.0.1:6400/parser?url=https://pan.huang1111.cn/s/wDz5TK
|
||||
|
||||
|
||||
|
||||
```
|
||||
json返回数据格式示例:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"success": true,
|
||||
"count": 0,
|
||||
"data": {
|
||||
"shareKey": "lz:xxx",
|
||||
"directLink": "下载直链",
|
||||
"cacheHit": true,
|
||||
"expires": "2024-09-18 01:48:02",
|
||||
"expiration": 1726638482825
|
||||
},
|
||||
"timestamp": 1726637151902
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"success": true,
|
||||
"count": 0,
|
||||
"data": "https://下载链接",
|
||||
"timestamp": 1690733953927
|
||||
}
|
||||
```
|
||||
|
||||
@@ -118,17 +145,17 @@ GET http://127.0.0.1:6400/json/fc/e5079007dc31226096628870c7@QAIU
|
||||
# 网盘对比
|
||||
|
||||
|
||||
| 网盘名称 | 免登陆下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 |
|
||||
|-------------|---------|----------|-----------|-----------------|
|
||||
| 蓝奏云 | √ | √ | 不限空间 | 100M |
|
||||
| 奶牛快传 | √ | X | 10G | 不限大小 |
|
||||
| 移动云云空间(个人版) | √ | √(密码可忽略) | 5G(个人) | 不限大小 |
|
||||
| 小飞机网盘 | √ | √(密码可忽略) | 10G | 不限大小 |
|
||||
| 360亿方云 | √ | √(密码可忽略) | 100G(须实名) | 不限大小 |
|
||||
| 123云盘 | √ | √ | 2T | 100G(>100M需要登录) |
|
||||
| 文叔叔 | √ | √ | 10G | 5GB |
|
||||
| 夸克网盘 | x | √ | 10G | 不限大小 |
|
||||
| UC网盘 | x | √ | 10G | 不限大小 |
|
||||
| 网盘名称 | 可直接下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 | 登录接口 |
|
||||
|------------|------------------------|----------|-----------|-----------------|------|
|
||||
| 蓝奏云 | √ | √ | 不限空间 | 100M | TODO |
|
||||
| 奶牛快传 | √ | X | 10G | 不限大小 | TODO |
|
||||
| 移动云空间 | √ | √(密码可忽略) | 5G(个人) | 不限大小 | TODO |
|
||||
| UC网盘 | 需要登录 | √ | 10G | 不限大小 | TODO |
|
||||
| 小飞机网盘 | √ | √(密码可忽略) | 10G | 不限大小 | TODO |
|
||||
| 360亿方云 | √(试用账号有时间限制企业版需要599续费) | √(密码可忽略) | 100G(须实名) | 不限大小 | TODO |
|
||||
| 123云盘 | √ | √ | 2T | 100G(>100M需要登录) | TODO |
|
||||
| 文叔叔(TODO) | √(注意有时间限制) | √ | 10G | 5GB | TODO |
|
||||
| 夸克网盘(TODO) | 需要登录 | √ | 10G | 不限大小 | TODO |
|
||||
|
||||
# 打包部署
|
||||
|
||||
@@ -136,7 +163,7 @@ GET http://127.0.0.1:6400/json/fc/e5079007dc31226096628870c7@QAIU
|
||||
- [阿里jdk17(Dragonwell17-windows-x86)](https://lz.qaiu.top/ec/e957acef36ce89e1053979672a90d219n)
|
||||
- [阿里jdk17(Dragonwell17-linux-x86)](https://lz.qaiu.top/ec/6ebc9f2e0bbd53b4c4d5b11013f40a80NHvcYU)
|
||||
- [阿里jdk17(Dragonwell17-linux-aarch64)](https://lz.qaiu.top/ec/d14c2d06296f61b52a876b525265e0f8tzxTc5)
|
||||
- [解析有效性测试-移动云云空间-阿里jdk17-linux-x86](https://lz.qaiu.top/json/ec/6ebc9f2e0bbd53b4c4d5b11013f40a80NHvcYU)
|
||||
- [解析有效性测试-移动云空间-阿里jdk17-linux-x86](https://lz.qaiu.top/json/ec/6ebc9f2e0bbd53b4c4d5b11013f40a80NHvcYU)
|
||||
|
||||
## 开发和打包
|
||||
|
||||
@@ -151,7 +178,7 @@ mvn package
|
||||
> 注意: netdisk-fast-download.service中的ExecStart的路径改为实际路径
|
||||
```shell
|
||||
cd ~
|
||||
wget -O netdisk-fast-download.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.8-release-fixed2/netdisk-fast-download-bin-fixed2.zip
|
||||
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
|
||||
cd netdisk-fast-download
|
||||
bash service-install.sh
|
||||
@@ -185,9 +212,10 @@ bash service-install.sh
|
||||
|
||||
## 0.1.8 开发计划
|
||||
- Docker部署
|
||||
- 联想乐云解析 √
|
||||
- 直链缓存 √
|
||||
- 日志优化 √
|
||||
- 联想乐云解析支持
|
||||
- CLoudreve解析解析
|
||||
- 直链缓存
|
||||
- 日志优化
|
||||
|
||||
|
||||
**技术栈:**
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
rem 获取当前 Java 版本信息并搜索是否包含 "17."
|
||||
java -version 2>&1 | find "17." >nul
|
||||
|
||||
rem 如果找不到 JDK 17.x,则下载并安装
|
||||
if errorlevel 1 (
|
||||
echo JDK 17.x not found. Downloading and installing...
|
||||
|
||||
REM 这里添加下载和安装 JDK 的代码
|
||||
|
||||
rem 验证安装
|
||||
java -version
|
||||
|
||||
echo JDK 17.x installation complete.
|
||||
) else (
|
||||
echo JDK 17.x is already installed.
|
||||
)
|
||||
|
||||
endlocal
|
||||
pause
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>netdisk-fast-download</artifactId>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>0.1.7</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>1.0.8</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package cn.qaiu.db.ddl;
|
||||
|
||||
import cn.qaiu.db.pool.JDBCType;
|
||||
import cn.qaiu.vx.core.util.ReflectionUtil;
|
||||
import io.vertx.codegen.format.CamelCase;
|
||||
import io.vertx.codegen.format.Case;
|
||||
@@ -66,9 +65,7 @@ public class CreateTable {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getCreateTableSQL(Class<?> clz, JDBCType type) {
|
||||
String quotationMarks = type == JDBCType.H2DB ? "\"" : "`";
|
||||
String endStr = type == JDBCType.H2DB ? ");" : ")ENGINE=InnoDB DEFAULT CHARSET=utf8;";
|
||||
public static String getCreateTableSQL(Class<?> clz) {
|
||||
// 判断类上是否有次注解
|
||||
String primaryKey = null; // 主键
|
||||
String tableName = null; // 表名
|
||||
@@ -96,7 +93,7 @@ public class CreateTable {
|
||||
int[] decimalSize = {22, 2};
|
||||
int varcharSize = 255;
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
sb.append("CREATE TABLE IF NOT EXISTS ").append(quotationMarks).append(tableName).append(quotationMarks).append(" ( \r\n ");
|
||||
sb.append("CREATE TABLE IF NOT EXISTS \"").append(tableName).append("\" ( \r\n ");
|
||||
boolean firstId = true;
|
||||
for (Field f : fields) {
|
||||
Class<?> paramType = f.getType();
|
||||
@@ -117,7 +114,7 @@ public class CreateTable {
|
||||
decimalSize = fieldAnnotation.decimalSize();
|
||||
varcharSize = fieldAnnotation.varcharSize();
|
||||
}
|
||||
sb.append(quotationMarks).append(column).append(quotationMarks);
|
||||
sb.append("\"").append(column).append("\"");
|
||||
sb.append(" ").append(sqlType);
|
||||
// 添加类型长度
|
||||
if (sqlType.equals("DECIMAL")) {
|
||||
@@ -158,20 +155,17 @@ public class CreateTable {
|
||||
//去掉最后一个逗号
|
||||
int lastIndex = sql.lastIndexOf(",");
|
||||
sql = sql.substring(0, lastIndex) + sql.substring(lastIndex + 1);
|
||||
return sql.substring(0, sql.length() - 1) + endStr;
|
||||
return sql.substring(0, sql.length() - 1) + ");\r\n";
|
||||
}
|
||||
|
||||
public static void createTable(JDBCPool pool, JDBCType type) {
|
||||
public static void createTable(JDBCPool pool) {
|
||||
Set<Class<?>> tableClassList = ReflectionUtil.getReflections().getTypesAnnotatedWith(Table.class);
|
||||
if (tableClassList.isEmpty()) LOGGER.info("Table model class not fount");
|
||||
tableClassList.forEach(clazz -> {
|
||||
String createTableSQL = getCreateTableSQL(clazz, type);
|
||||
|
||||
String createTableSQL = getCreateTableSQL(clazz);
|
||||
pool.query(createTableSQL).execute().onSuccess(
|
||||
rs -> LOGGER.info("table auto generate:\n" + createTableSQL)
|
||||
).onFailure(e -> {
|
||||
LOGGER.error(e.getMessage() + " SQL: \n" + createTableSQL);
|
||||
});
|
||||
rs -> LOGGER.info("\n" + createTableSQL + "create table --> ok")
|
||||
).onFailure(Throwable::printStackTrace);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
package cn.qaiu.db.pool;
|
||||
|
||||
import cn.qaiu.db.ddl.CreateTable;
|
||||
import cn.qaiu.db.server.H2ServerHolder;
|
||||
import cn.qaiu.vx.core.util.VertxHolder;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.jdbcclient.JDBCPool;
|
||||
import org.h2.tools.Server;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* 初始化JDBC
|
||||
* <br>Create date 2021/8/10 12:04
|
||||
@@ -21,14 +28,12 @@ public class JDBCPoolInit {
|
||||
JsonObject dbConfig;
|
||||
Vertx vertx = VertxHolder.getVertxInstance();
|
||||
String url;
|
||||
private JDBCType type;
|
||||
|
||||
private static JDBCPoolInit instance;
|
||||
|
||||
public JDBCPoolInit(Builder builder) {
|
||||
this.dbConfig = builder.dbConfig;
|
||||
this.url = builder.url;
|
||||
this.type = builder.type;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
@@ -42,12 +47,10 @@ public class JDBCPoolInit {
|
||||
public static class Builder {
|
||||
private JsonObject dbConfig;
|
||||
private String url;
|
||||
private JDBCType type;
|
||||
|
||||
public Builder config(JsonObject dbConfig) {
|
||||
this.dbConfig = dbConfig;
|
||||
this.url = dbConfig.getString("jdbcUrl");
|
||||
this.type = JDBCUtil.getJDBCType(dbConfig.getString("driverClassName"));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -70,16 +73,67 @@ public class JDBCPoolInit {
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化数据库连接
|
||||
vertx.createSharedWorkerExecutor("sql-pool-init")
|
||||
.executeBlocking(() -> {
|
||||
// 初始化连接池
|
||||
pool = JDBCPool.pool(vertx, dbConfig);
|
||||
CreateTable.createTable(pool, type);
|
||||
return "数据库连接初始化: URL=" + url;
|
||||
// 异步启动H2服务
|
||||
vertx.createSharedWorkerExecutor("h2-server", 1, Long.MAX_VALUE)
|
||||
.executeBlocking(this::h2serverExecute)
|
||||
.onSuccess(res->{
|
||||
LOGGER.info(res);
|
||||
// 初始化数据库连接
|
||||
vertx.createSharedWorkerExecutor("sql-pool-init")
|
||||
.executeBlocking(this::poolInitExecute)
|
||||
.onSuccess(LOGGER::info)
|
||||
.onFailure(Throwable::printStackTrace);
|
||||
})
|
||||
.onSuccess(LOGGER::info)
|
||||
.onFailure(Throwable::printStackTrace);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void poolInitExecute(Promise<String> promise) {
|
||||
// 初始化连接池
|
||||
pool = JDBCPool.pool(vertx, dbConfig);
|
||||
CreateTable.createTable(pool);
|
||||
promise.complete("init jdbc pool success");
|
||||
|
||||
}
|
||||
|
||||
private void checkOrCreateDBFile() {
|
||||
LOGGER.info("init sql start");
|
||||
String[] path = url.split("\\./");
|
||||
path[1] = path[1].split(";")[0];
|
||||
path[1] += ".mv.db";
|
||||
File file = new File(path[1]);
|
||||
if (!file.exists()) {
|
||||
if (!file.getParentFile().exists()) {
|
||||
if (file.getParentFile().mkdirs()) {
|
||||
LOGGER.info("mkdirs -> {}", file.getParentFile().getAbsolutePath());
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (file.createNewFile()) {
|
||||
LOGGER.info("create file -> {}", file.getAbsolutePath());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(e.getMessage());
|
||||
throw new RuntimeException("file create failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void h2serverExecute(Promise<String> promise) {
|
||||
// 初始化H2db, 创建本地db文件
|
||||
checkOrCreateDBFile();
|
||||
|
||||
try {
|
||||
String url = dbConfig.getString("jdbcUrl");
|
||||
String[] portStr = url.split(":");
|
||||
String port = portStr[portStr.length - 1].split("[/\\\\]")[0];
|
||||
LOGGER.info("H2server listen port to {}", port);
|
||||
H2ServerHolder.init(Server.createTcpServer("-tcp", "-tcpAllowOthers", "-tcpPort", port).start());
|
||||
promise.complete("Start h2Server success");
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Start h2Server failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package cn.qaiu.db.pool;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2023/10/10 14:06
|
||||
*/
|
||||
public enum JDBCType {
|
||||
MySQL, H2DB
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package cn.qaiu.db.pool;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2023/10/10 14:05
|
||||
*/
|
||||
public class JDBCUtil {
|
||||
public static JDBCType getJDBCType(String deviceName) {
|
||||
switch (deviceName) {
|
||||
case "com.mysql.cj.jdbc.Driver":
|
||||
case "com.mysql.jdbc.Driver":
|
||||
return JDBCType.MySQL;
|
||||
case "org.h2.Driver":
|
||||
return JDBCType.H2DB;
|
||||
}
|
||||
throw new RuntimeException("不支持的SQL驱动类型: " + deviceName);
|
||||
}
|
||||
}
|
||||
23
core/pom.xml
23
core/pom.xml
@@ -5,15 +5,28 @@
|
||||
<parent>
|
||||
<artifactId>netdisk-fast-download</artifactId>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>0.1.7</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<version>1.0.8</version>
|
||||
<artifactId>core</artifactId>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-dependencies</artifactId>
|
||||
<version>${vertx.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!--logback日志实现-->
|
||||
<dependency>
|
||||
@@ -60,13 +73,11 @@
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.melloware/commons-beanutils2 -->
|
||||
<dependency>
|
||||
<groupId>com.melloware</groupId>
|
||||
<artifactId>commons-beanutils2</artifactId>
|
||||
<version>${commons-beanutils2.version}</version>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>1.9.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
|
||||
@@ -32,14 +32,12 @@ import org.slf4j.LoggerFactory;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.qaiu.vx.core.util.ConfigConstant.ROUTE_TIME_OUT;
|
||||
import static io.vertx.core.http.HttpHeaders.*;
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||
|
||||
/**
|
||||
* 路由映射, 参数绑定
|
||||
@@ -89,7 +87,6 @@ public class RouterHandlerFactory implements BaseHttpApi {
|
||||
LOGGER.debug("The HTTP service request address information ===>path:{}, uri:{}, method:{}",
|
||||
ctx.request().path(), ctx.request().absoluteURI(), ctx.request().method());
|
||||
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
ctx.response().headers().add(DATE, LocalDateTime.now().format(ISO_LOCAL_DATE_TIME));
|
||||
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, OPTIONS, PUT, DELETE, HEAD");
|
||||
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_HEADERS, "X-PINGOTHER, Origin,Content-Type, Accept, " +
|
||||
"X-Requested-With, Dev, Authorization, Version, Token");
|
||||
|
||||
@@ -3,8 +3,8 @@ package cn.qaiu.vx.core.util;
|
||||
import cn.qaiu.vx.core.annotaions.HandleSortFilter;
|
||||
import io.vertx.core.buffer.Buffer;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import org.apache.commons.beanutils2.ConvertUtils;
|
||||
import org.apache.commons.beanutils2.Converter;
|
||||
import org.apache.commons.beanutils.ConvertUtils;
|
||||
import org.apache.commons.beanutils.Converter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ public interface ConfigConstant {
|
||||
String EVENT_LOOP_POOL_SIZE = "eventLoopPoolSize";
|
||||
String LOCAL = "local";
|
||||
String SERVER = "server";
|
||||
String CACHE = "cache";
|
||||
String GLOBAL_CONFIG = "globalConfig";
|
||||
String CUSTOM_CONFIG = "customConfig";
|
||||
String ASYNC_SERVICE_INSTANCES = "asyncServiceInstances";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.qaiu.vx.core.util;
|
||||
|
||||
import io.vertx.core.MultiMap;
|
||||
import org.apache.commons.beanutils2.BeanUtils;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@@ -5,13 +5,12 @@ import javassist.bytecode.AccessFlag;
|
||||
import javassist.bytecode.CodeAttribute;
|
||||
import javassist.bytecode.LocalVariableAttribute;
|
||||
import javassist.bytecode.MethodInfo;
|
||||
import org.apache.commons.beanutils.ConversionException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.reflections.Reflections;
|
||||
import org.reflections.scanners.MemberUsageScanner;
|
||||
import org.reflections.scanners.MethodParameterNamesScanner;
|
||||
import org.reflections.scanners.Scanners;
|
||||
import org.reflections.scanners.*;
|
||||
import org.reflections.util.ClasspathHelper;
|
||||
import org.reflections.util.ConfigurationBuilder;
|
||||
import org.reflections.util.FilterBuilder;
|
||||
@@ -186,10 +185,10 @@ public final class ReflectionUtil {
|
||||
return DateUtils.parseDate(value, fmt);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("无法将格式化日期");
|
||||
throw new ConversionException("无法将格式化日期");
|
||||
}
|
||||
default:
|
||||
throw new RuntimeException("无法将String类型" + value + "转为[" + name + "]");
|
||||
throw new ConversionException("无法将String类型" + value + "转为[" + name + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +200,7 @@ public final class ReflectionUtil {
|
||||
* @return Array
|
||||
*/
|
||||
public static Object conversionArray(CtClass ctClass, String value) {
|
||||
if (!isBasicTypeArray(ctClass)) throw new RuntimeException("无法解析数组");
|
||||
if (!isBasicTypeArray(ctClass)) throw new ConversionException("无法解析数组");
|
||||
String[] strArr = value.split(",");
|
||||
List<Object> obj = new ArrayList<>();
|
||||
Arrays.stream(strArr).forEach(v -> obj.add(conversion(ctClass, v, null)));
|
||||
|
||||
310
mvnw
vendored
310
mvnw
vendored
@@ -1,310 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=`cd "$wdir/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||
fi
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||
fi
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
else
|
||||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
fi
|
||||
while IFS="=" read key value; do
|
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||
esac
|
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Downloading from: $jarUrl"
|
||||
fi
|
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if $cygwin; then
|
||||
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found wget ... using wget"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget "$jarUrl" -O "$wrapperJarPath"
|
||||
else
|
||||
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found curl ... using curl"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||
else
|
||||
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||
fi
|
||||
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Falling back to using Java to download"
|
||||
fi
|
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaClass=`cygpath --path --windows "$javaClass"`
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
# Compiling the Java class
|
||||
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||
fi
|
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
# Running the downloader
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Running MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
fi
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
182
mvnw.cmd
vendored
182
mvnw.cmd
vendored
@@ -1,182 +0,0 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
|
||||
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %DOWNLOAD_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
||||
24
note.txt
24
note.txt
@@ -1,24 +0,0 @@
|
||||
直链缓存设计
|
||||
每个网盘对应的标准分享URL如下
|
||||
|
||||
蓝奏云 (lz) https://lanzoux.com/{shareKey}
|
||||
蓝奏云优享 (iz) https://www.ilanzou.com/s/{shareKey}
|
||||
奶牛快传 (cow) https://cowtransfer.com/s/{shareKey}
|
||||
移动云云空间 (ec) https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={shareKey}&isShare=1
|
||||
小飞机网盘 (fj) https://share.feijipan.com/s/{shareKey}
|
||||
亿方云 (fc) https://v2.fangcloud.com/sharing/{shareKey}
|
||||
123云盘 (ye) https://www.123pan.com/s/{shareKey}.html
|
||||
文叔叔 (ws) https://f.ws59.cn/f/{shareKey}
|
||||
联想乐云 (le) https://lecloud.lenovo.com/share/{shareKey}
|
||||
|
||||
私有化网盘需要自己的域名也就是origin地址.
|
||||
Cloudreve自建网盘 (ce) {origin}/s/{shareKey}
|
||||
|
||||
分享URL -> 类型+key
|
||||
类型+key -> 标准分享URL
|
||||
|
||||
缓存key -> 下载URL
|
||||
分享链接 -> add 网盘类型 pwd origin(私有化) -> 直链
|
||||
|
||||
|
||||
https://f.ws59.cn/f/e3peohu6192
|
||||
@@ -2,12 +2,13 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>netdisk-fast-download</artifactId>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>0.1.7</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>parser</artifactId>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
package cn.qaiu.entity;
|
||||
|
||||
public class ShareLinkInfo {
|
||||
|
||||
private String shareKey; // 分享键
|
||||
private String type; // 分享类型
|
||||
private String sharePassword; // 分享密码(如果存在)
|
||||
private String shareUrl; // 原始分享链接
|
||||
private String standardUrl; // 规范化的标准链接
|
||||
|
||||
private ShareLinkInfo(Builder builder) {
|
||||
this.shareKey = builder.shareKey;
|
||||
this.type = builder.type;
|
||||
this.sharePassword = builder.sharePassword;
|
||||
this.shareUrl = builder.shareUrl;
|
||||
this.standardUrl = builder.standardUrl;
|
||||
}
|
||||
|
||||
// Getter和Setter方法
|
||||
|
||||
public String getShareKey() {
|
||||
return shareKey;
|
||||
}
|
||||
|
||||
public void setShareKey(String shareKey) {
|
||||
this.shareKey = shareKey;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getSharePassword() {
|
||||
return sharePassword;
|
||||
}
|
||||
|
||||
public void setSharePassword(String sharePassword) {
|
||||
this.sharePassword = sharePassword;
|
||||
}
|
||||
|
||||
public String getShareUrl() {
|
||||
return shareUrl;
|
||||
}
|
||||
|
||||
public void setShareUrl(String shareUrl) {
|
||||
this.shareUrl = shareUrl;
|
||||
}
|
||||
|
||||
public String getStandardUrl() {
|
||||
return standardUrl;
|
||||
}
|
||||
|
||||
public void setStandardUrl(String standardUrl) {
|
||||
this.standardUrl = standardUrl;
|
||||
}
|
||||
|
||||
// 静态方法创建建造者对象
|
||||
public static ShareLinkInfo.Builder newBuilder() {
|
||||
return new ShareLinkInfo.Builder();
|
||||
}
|
||||
|
||||
// 建造者类
|
||||
public static class Builder {
|
||||
private String shareKey; // 分享键
|
||||
private String type; // 分享类型 (网盘模板枚举的小写)
|
||||
private String sharePassword = ""; // 分享密码(如果存在)
|
||||
private String shareUrl; // 原始分享链接
|
||||
private String standardUrl; // 规范化的标准链接
|
||||
|
||||
public Builder shareKey(String shareKey) {
|
||||
this.shareKey = shareKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder type(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sharePassword(String sharePassword) {
|
||||
this.sharePassword = sharePassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder shareUrl(String shareUrl) {
|
||||
this.shareUrl = shareUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder standardUrl(String standardUrl) {
|
||||
this.standardUrl = standardUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShareLinkInfo build() {
|
||||
return new ShareLinkInfo(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ShareLinkInfo{" +
|
||||
"shareKey='" + shareKey + '\'' +
|
||||
", type='" + type + '\'' +
|
||||
", sharePassword='" + sharePassword + '\'' +
|
||||
", shareUrl='" + shareUrl + '\'' +
|
||||
", standardUrl='" + standardUrl + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,62 @@
|
||||
package cn.qaiu.parser;//package cn.qaiu.lz.common.parser;
|
||||
|
||||
import cn.qaiu.parser.impl.*;
|
||||
import io.vertx.core.Future;
|
||||
|
||||
public interface IPanTool {
|
||||
Future<String> parse();
|
||||
|
||||
static IPanTool typeMatching(String type, String key, String pwd) {
|
||||
return switch (type) {
|
||||
case "lz" -> new LzTool(key, pwd);
|
||||
case "cow" -> new CowTool(key, pwd);
|
||||
case "ec" -> new EcTool(key, pwd);
|
||||
case "fc" -> new FcTool(key, pwd);
|
||||
case "uc" -> new UcTool(key, pwd);
|
||||
case "ye" -> new YeTool(key, pwd);
|
||||
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);
|
||||
case "iz" -> new IzTool(key, pwd);
|
||||
case "ce" -> new CeTool(key, pwd);
|
||||
default -> {
|
||||
throw new UnsupportedOperationException("未知分享类型");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static IPanTool shareURLPrefixMatching(String url, String pwd) {
|
||||
|
||||
if (url.contains(CowTool.LINK_KEY)) {
|
||||
return new CowTool(url, pwd);
|
||||
} else if (url.startsWith(EcTool.SHARE_URL_PREFIX)) {
|
||||
return new EcTool(url, pwd);
|
||||
} else if (url.startsWith(FcTool.SHARE_URL_PREFIX0)) {
|
||||
return new FcTool(url, pwd);
|
||||
} else if (url.startsWith(UcTool.SHARE_URL_PREFIX)) {
|
||||
return new UcTool(url, pwd);
|
||||
} else if (url.startsWith(YeTool.SHARE_URL_PREFIX)) {
|
||||
return new YeTool(url, pwd);
|
||||
} else if (url.startsWith(FjTool.SHARE_URL_PREFIX) || url.startsWith(FjTool.SHARE_URL_PREFIX2)) {
|
||||
return new FjTool(url, pwd);
|
||||
} else if (url.startsWith(IzTool.SHARE_URL_PREFIX)) {
|
||||
return new IzTool(url, pwd);
|
||||
} else if (url.contains(LzTool.LINK_KEY)) {
|
||||
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);
|
||||
} else if (url.contains("/s/")) {
|
||||
// Cloudreve 网盘通用解析
|
||||
return new CeTool(url, pwd);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("未知分享类型");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import cn.qaiu.WebClientVertxInit;
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
@@ -35,7 +35,16 @@ public abstract class PanBase {
|
||||
protected WebClient clientNoRedirects = WebClient.create(WebClientVertxInit.get(),
|
||||
new WebClientOptions().setFollowRedirects(false));
|
||||
|
||||
protected ShareLinkInfo shareLinkInfo;
|
||||
/**
|
||||
* 分享key 可以是整个URL; 如果是URL实现该类时要
|
||||
* 使用{@link CommonUtils#adaptShortPaths(String urlPrefix, String key)}获取真实的分享key
|
||||
*/
|
||||
protected String key;
|
||||
|
||||
/**
|
||||
* 分享密码
|
||||
*/
|
||||
protected String pwd;
|
||||
|
||||
/**
|
||||
* 子类重写此构造方法不需要添加额外逻辑
|
||||
@@ -46,9 +55,12 @@ public abstract class PanBase {
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param key 分享key/url
|
||||
* @param pwd 分享密码
|
||||
*/
|
||||
public PanBase(ShareLinkInfo shareLinkInfo) {
|
||||
this.shareLinkInfo = shareLinkInfo;
|
||||
protected PanBase(String key, String pwd) {
|
||||
this.key = key;
|
||||
this.pwd = pwd;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import cn.qaiu.parser.impl.*;
|
||||
|
||||
/**
|
||||
* 枚举类 PanDomainTemplate 定义了不同网盘服务的模板信息,包括:
|
||||
* <ul>
|
||||
* <li>displayName: 网盘服务的显示名称,用于用户界面展示。</li>
|
||||
* <li>regexPattern: 用于匹配和解析分享链接的正则表达式。</li>
|
||||
* <li>standardUrlTemplate: 网盘服务的标准URL模板,用于规范化分享链接。</li>
|
||||
* <li>toolClass: 网盘解析工具实现类。</li>
|
||||
* </ul>
|
||||
* 请注意:增添网盘时,保证正则表达式最后一个捕捉组能匹配到分享key
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* at 2023/6/13 4:26
|
||||
*/
|
||||
public enum PanDomainTemplate {
|
||||
|
||||
// 网盘定义
|
||||
LZ("蓝奏云",
|
||||
"https://([a-z]+)?\\.?lanzou[a-z]\\.com/(.+/)?(.+)",
|
||||
"https://lanzoux.com/{shareKey}",
|
||||
LzTool.class),
|
||||
|
||||
// https://www.feijix.com/s/
|
||||
// https://share.feijipan.com/s/
|
||||
FJ("小飞机网盘",
|
||||
"https://(share\\.feijipan\\.com|www\\.feijix\\.com)/s/(.+)",
|
||||
"https://www.feijix.com/s/{shareKey}",
|
||||
FjTool.class),
|
||||
|
||||
// https://lecloud.lenovo.com/share/
|
||||
LE("联想乐云",
|
||||
"https://lecloud?\\.lenovo\\.com/share/(.+)",
|
||||
"https://lecloud.lenovo.com/share/{shareKey}",
|
||||
LeTool.class),
|
||||
|
||||
// https://v2.fangcloud.com/s/
|
||||
FC("亿方云",
|
||||
"https://v2\\.fangcloud\\.(com|cn)/(s|sharing)/([^/]+)",
|
||||
"https://v2.fangcloud.com/s/{shareKey}",
|
||||
FcTool.class),
|
||||
// https://www.ilanzou.com/s/
|
||||
IZ("蓝奏云优享",
|
||||
"https://www\\.ilanzou\\.com/s/(.+)",
|
||||
"https://www.ilanzou.com/s/{shareKey}",
|
||||
IzTool.class),
|
||||
// https://wx.mail.qq.com/ftn/download?
|
||||
QQ("QQ邮箱中转站",
|
||||
"https://i?wx\\.mail\\.qq\\.com/ftn/download\\?(.+)",
|
||||
"https://iwx.mail.qq.com/ftn/download/{shareKey}",
|
||||
QQTool.class),
|
||||
// https://f.ws59.cn/f/或者https://www.wenshushu.cn/f/
|
||||
WS("文叔叔",
|
||||
"https://(f\\.ws59\\.cn|www\\.wenshushu\\.cn)/f/(.+)",
|
||||
"https://f.ws59.cn/f/{shareKey}",
|
||||
WsTool.class),
|
||||
// https://www.123pan.com/s/
|
||||
YE("123网盘",
|
||||
"https://www\\.123pan\\.com/s/(.+)",
|
||||
"https://www.123pan.com/s/{shareKey}",
|
||||
YeTool.class),
|
||||
// https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={code}&isShare=1
|
||||
EC("移动云空间",
|
||||
"https://www\\.ecpan\\.cn/web(/%23|/#)?/yunpanProxy\\?path=.*&data=" +
|
||||
"([^&]+)&isShare=1",
|
||||
"https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={shareKey}&isShare=1",
|
||||
EcTool.class),
|
||||
// https://cowtransfer.com/s/
|
||||
COW("奶牛快传",
|
||||
"https://(.*)cowtransfer\\.com/s/(.+)",
|
||||
"https://cowtransfer.com/s/{shareKey}",
|
||||
CowTool.class),
|
||||
// https://pan.huang1111.cn/s/
|
||||
CE("huang1111",
|
||||
"https://pan\\.huang1111\\.cn/s/(.+)",
|
||||
"https://pan.huang1111.cn/s/{shareKey}",
|
||||
CeTool.class);
|
||||
|
||||
|
||||
// 网盘的显示名称,用于用户界面显示
|
||||
private final String displayName;
|
||||
|
||||
// 用于匹配和解析分享链接的正则表达式,保证最后一个捕捉组能匹配到分享key
|
||||
private final String regexPattern;
|
||||
|
||||
// 网盘的标准链接模板,不含占位符,用于规范化分享链接
|
||||
private final String standardUrlTemplate;
|
||||
|
||||
// 指向解析工具IPanTool实现类
|
||||
private final Class<? extends IPanTool> toolClass;
|
||||
|
||||
PanDomainTemplate(String displayName, String regexPattern, String standardUrlTemplate,
|
||||
Class<? extends IPanTool> toolClass) {
|
||||
this.displayName = displayName;
|
||||
this.regexPattern = regexPattern;
|
||||
this.standardUrlTemplate = standardUrlTemplate;
|
||||
this.toolClass = toolClass;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getRegexPattern() {
|
||||
return regexPattern;
|
||||
}
|
||||
|
||||
public String getStandardUrlTemplate() {
|
||||
return standardUrlTemplate;
|
||||
}
|
||||
|
||||
public Class<? extends IPanTool> getToolClass() {
|
||||
return toolClass;
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* 该类提供方法来解析和规范化不同来源的分享链接,确保它们可以转换为统一的标准链接格式。
|
||||
* 通过这种方式,应用程序可以更容易地处理和识别不同网盘服务的分享链接。
|
||||
*
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/15 14:10
|
||||
*/
|
||||
public class ParserCreate {
|
||||
private final PanDomainTemplate panDomainTemplate;
|
||||
private final ShareLinkInfo shareLinkInfo;
|
||||
|
||||
public ParserCreate(PanDomainTemplate panDomainTemplate, ShareLinkInfo shareLinkInfo) {
|
||||
this.panDomainTemplate = panDomainTemplate;
|
||||
this.shareLinkInfo = shareLinkInfo;
|
||||
}
|
||||
|
||||
|
||||
// 解析并规范化分享链接
|
||||
public ParserCreate normalizeShareLink() {
|
||||
if (shareLinkInfo == null) {
|
||||
throw new IllegalArgumentException("ShareLinkInfo not init");
|
||||
}
|
||||
// 匹配并提取shareKey
|
||||
String shareUrl = shareLinkInfo.getShareUrl();
|
||||
if (StringUtils.isEmpty(shareUrl)) {
|
||||
throw new IllegalArgumentException("ShareLinkInfo shareUrl is empty");
|
||||
}
|
||||
Pattern pattern = Pattern.compile(this.panDomainTemplate.getRegexPattern());
|
||||
Matcher matcher = pattern.matcher(shareUrl);
|
||||
if (matcher.find()) {
|
||||
String shareKey = matcher.group(matcher.groupCount());
|
||||
// 返回规范化的标准链接
|
||||
String standardUrl = getStandardUrlTemplate().replace("{shareKey}", shareKey);
|
||||
shareLinkInfo.setShareUrl(shareUrl);
|
||||
shareLinkInfo.setShareKey(shareKey);
|
||||
shareLinkInfo.setStandardUrl(standardUrl);
|
||||
return this;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid share URL for " + this.panDomainTemplate.getDisplayName());
|
||||
}
|
||||
|
||||
public IPanTool createTool() {
|
||||
if (shareLinkInfo == null || StringUtils.isEmpty(shareLinkInfo.getType())) {
|
||||
throw new IllegalArgumentException("ShareLinkInfo not init or type is empty");
|
||||
}
|
||||
if (StringUtils.isEmpty(shareLinkInfo.getShareKey())) {
|
||||
this.normalizeShareLink();
|
||||
}
|
||||
try {
|
||||
return this.panDomainTemplate.getToolClass()
|
||||
.getDeclaredConstructor(ShareLinkInfo.class)
|
||||
.newInstance(shareLinkInfo);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("无法创建工具实例: " + panDomainTemplate.getToolClass().getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// set share key
|
||||
public ParserCreate shareKey(String shareKey) {
|
||||
shareLinkInfo.setShareKey(shareKey);
|
||||
shareLinkInfo.setStandardUrl(panDomainTemplate.getStandardUrlTemplate().replace("{shareKey}", shareKey));
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getStandardUrlTemplate() {
|
||||
return this.panDomainTemplate.getStandardUrlTemplate();
|
||||
}
|
||||
|
||||
public ShareLinkInfo getShareLinkInfo() {
|
||||
return shareLinkInfo;
|
||||
}
|
||||
|
||||
public ParserCreate setShareLinkInfoPwd(String pwd) {
|
||||
shareLinkInfo.setSharePassword(pwd);
|
||||
return this;
|
||||
}
|
||||
|
||||
// 根据分享链接获取PanDomainTemplate实例
|
||||
public synchronized static ParserCreate fromShareUrl(String shareUrl) {
|
||||
for (PanDomainTemplate panDomainTemplate : PanDomainTemplate.values()) {
|
||||
if (shareUrl.matches(panDomainTemplate.getRegexPattern())) {
|
||||
ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder()
|
||||
.type(panDomainTemplate.name().toLowerCase())
|
||||
.shareUrl(shareUrl).build();
|
||||
ParserCreate parserCreate = new ParserCreate(panDomainTemplate, shareLinkInfo);
|
||||
return parserCreate.normalizeShareLink();
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported share URL");
|
||||
}
|
||||
|
||||
// 根据type获取枚举实例
|
||||
public synchronized static ParserCreate fromType(String type) {
|
||||
try {
|
||||
PanDomainTemplate panDomainTemplate = Enum.valueOf(PanDomainTemplate.class, type.toUpperCase());
|
||||
ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder()
|
||||
.type(type.toLowerCase()).build();
|
||||
return new ParserCreate(panDomainTemplate, shareLinkInfo);
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
// 如果没有找到对应的枚举实例,抛出异常
|
||||
throw new IllegalArgumentException("No enum constant for type name: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import io.vertx.core.Future;
|
||||
@@ -8,6 +7,9 @@ import io.vertx.core.buffer.Buffer;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.HttpRequest;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* <a href="https://github.com/cloudreve/Cloudreve">Cloudreve网盘解析</a> <br>
|
||||
* <a href="https://pan.xiaomuxi.cn">暮希云盘</a> <br>
|
||||
@@ -15,40 +17,34 @@ import io.vertx.ext.web.client.HttpRequest;
|
||||
*/
|
||||
public class CeTool extends PanBase implements IPanTool {
|
||||
|
||||
private static final String DOWNLOAD_API_PATH = "https://pan.huang1111.cn/api/v3/share/download/";
|
||||
private static final String DOWNLOAD_API_PATH = "/api/v3/share/download/";
|
||||
|
||||
// api/v3/share/info/g31PcQ?password=qaiu
|
||||
private static final String SHARE_API_PATH = "https://pan.huang1111.cn/api/v3/share/info/";
|
||||
private static final String SHARE_API_PATH = "/api/v3/share/info/";
|
||||
|
||||
public CeTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public CeTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
|
||||
public Future<String> parse() {
|
||||
String key = shareLinkInfo.getShareKey();
|
||||
String pwd = shareLinkInfo.getSharePassword();
|
||||
// https://pan.huang1111.cn/s/wDz5TK
|
||||
// https://pan.huang1111.cn/s/y12bI6 -> https://pan.huang1111
|
||||
// .cn/api/v3/share/download/y12bI6?path=undefined%2Fundefined;
|
||||
// 类型解析 -> /ce/https_pan.huang1111.cn_s_wDz5TK
|
||||
// parser接口 -> /parser?url=https://pan.huang1111.cn/s/wDz5TK
|
||||
try {
|
||||
// if (key.startsWith("https_") || key.startsWith("http_")) {
|
||||
// key = key.replace("https_", "https://")
|
||||
// .replace("http_", "http://")
|
||||
// .replace("_", "/");
|
||||
// }
|
||||
// // 处理URL
|
||||
// URL url = new URL(key);
|
||||
// String path = url.getPath();
|
||||
// String shareKey = path.substring(3);
|
||||
// String downloadApiUrl = url.getProtocol() + "://" + url.getHost() + DOWNLOAD_API_PATH + shareKey + "?path" +
|
||||
// "=undefined/undefined;";
|
||||
// String shareApiUrl = url.getProtocol() + "://" + url.getHost() + SHARE_API_PATH + shareKey;
|
||||
|
||||
var shareApiUrl = SHARE_API_PATH;
|
||||
var downloadApiUrl = DOWNLOAD_API_PATH;
|
||||
if (key.startsWith("https_") || key.startsWith("http_")) {
|
||||
key = key.replace("https_", "https://")
|
||||
.replace("http_", "http://")
|
||||
.replace("_", "/");
|
||||
}
|
||||
// 处理URL
|
||||
URL url = new URL(key);
|
||||
String path = url.getPath();
|
||||
String shareKey = path.substring(3);
|
||||
String downloadApiUrl = url.getProtocol() + "://" + url.getHost() + DOWNLOAD_API_PATH + shareKey + "?path" +
|
||||
"=undefined/undefined;";
|
||||
String shareApiUrl = url.getProtocol() + "://" + url.getHost() + SHARE_API_PATH + shareKey;
|
||||
|
||||
// 设置cookie
|
||||
HttpRequest<Buffer> httpRequest = clientSession.getAbs(shareApiUrl);
|
||||
@@ -57,7 +53,7 @@ public class CeTool extends PanBase implements IPanTool {
|
||||
}
|
||||
// 获取下载链接
|
||||
httpRequest.send().onSuccess(res -> getDownURL(downloadApiUrl)).onFailure(handleFail(shareApiUrl));
|
||||
} catch (Exception e) {
|
||||
} catch (MalformedURLException e) {
|
||||
fail(e, "URL解析错误");
|
||||
}
|
||||
return promise.future();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -16,14 +16,16 @@ import org.apache.commons.lang3.StringUtils;
|
||||
public class CowTool extends PanBase implements IPanTool {
|
||||
|
||||
private static final String API_REQUEST_URL = "https://cowtransfer.com/core/api/transfer/share";
|
||||
public static final String SHARE_URL_PREFIX = "https://cowtransfer.com/s/";
|
||||
|
||||
public CowTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public static final String LINK_KEY = "cowtransfer.com/s/";
|
||||
|
||||
public CowTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
|
||||
public Future<String> parse() {
|
||||
final String key = shareLinkInfo.getShareKey();
|
||||
key = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
String url = API_REQUEST_URL + "?uniqueUrl=" + key;
|
||||
client.getAbs(url).send().onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
@@ -12,28 +12,22 @@ import io.vertx.uritemplate.UriTemplate;
|
||||
* 移动云空间解析
|
||||
*/
|
||||
public class EcTool extends PanBase implements IPanTool {
|
||||
// https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1
|
||||
private static final String FIRST_REQUEST_URL = "https://www.ecpan.cn/drive/fileextoverrid" +
|
||||
".do?extractionCode={extractionCode}&chainUrlTemplate=https:%2F%2Fwww.ecpan" +
|
||||
".do?chainUrlTemplate=https:%2F%2Fwww.ecpan" +
|
||||
".cn%2Fweb%2F%23%2FyunpanProxy%3Fpath%3D%252F%2523%252Fdrive%252Foutside&parentId=-1&data={dataKey}";
|
||||
|
||||
private static final String DOWNLOAD_REQUEST_URL = "https://www.ecpan.cn/drive/sharedownload.do";
|
||||
|
||||
public EcTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public static final String SHARE_URL_PREFIX = "www.ecpan.cn/";
|
||||
|
||||
public EcTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
|
||||
public Future<String> parse() {
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
// 第一次请求 获取文件信息
|
||||
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL))
|
||||
.setTemplateParam("dataKey", dataKey)
|
||||
.setTemplateParam("extractionCode", pwd == null ? "" : pwd)
|
||||
.send()
|
||||
.onSuccess(res -> {
|
||||
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("dataKey", dataKey).send().onSuccess(res -> {
|
||||
JsonObject jsonObject = res.bodyAsJsonObject();
|
||||
log.debug("ecPan get file info -> {}", jsonObject);
|
||||
JsonObject fileInfo = jsonObject
|
||||
@@ -43,11 +37,6 @@ public class EcTool extends PanBase implements IPanTool {
|
||||
fail("{} 解析失败:{} key = {}", FIRST_REQUEST_URL, fileInfo.getString("errMesg"), dataKey);
|
||||
return;
|
||||
}
|
||||
if (!fileInfo.containsKey("cloudpFile")) {
|
||||
fail("{} 解析失败:cloudpFile不存在 key = {}", FIRST_REQUEST_URL, dataKey);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject cloudpFile = fileInfo.getJsonObject("cloudpFile");
|
||||
JsonArray fileIdList = JsonArray.of(cloudpFile);
|
||||
// 构造请求JSON {"extCodeFlag":0,"isIp":0}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.Promise;
|
||||
@@ -21,19 +21,19 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class FcTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX0 = "https://v2.fangcloud.com/s";
|
||||
public static final String SHARE_URL_PREFIX = "https://v2.fangcloud.com/sharing/";
|
||||
public static final String SHARE_URL_PREFIX2 = "https://v2.fangcloud.cn/sharing/";
|
||||
private static final String DOWN_REQUEST_URL = "https://v2.fangcloud.cn/apps/files/download?file_id={fid}" +
|
||||
"&scenario=share&unique_name={uname}";
|
||||
|
||||
public FcTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public FcTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
|
||||
public Future<String> parse() {
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
String data = key.replace("share","sharing");
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
|
||||
WebClientSession sClient = WebClientSession.create(client);
|
||||
// 第一次请求 自动重定向
|
||||
sClient.getAbs(SHARE_URL_PREFIX + dataKey).send().onSuccess(res -> {
|
||||
|
||||
@@ -1,110 +1,90 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.AESUtils;
|
||||
import cn.qaiu.util.UUIDUtil;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.buffer.Buffer;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.HttpRequest;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 小飞机网盘
|
||||
*
|
||||
* @version V016_230609
|
||||
*/
|
||||
public class FjTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://www.feijix.com/s/";
|
||||
public static final String REFERER_URL = "https://share.feijipan.com/";
|
||||
public static final String SHARE_URL_PREFIX2 = REFERER_URL + "s/";
|
||||
private static final String API_URL_PREFIX = "https://api.feijipan.com/ws/";
|
||||
|
||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" +
|
||||
"&uuid={uuid}&extra=2×tamp={ts}&shareId={shareId}&type=0&offset=1&limit=60";
|
||||
/// recommend/list?devType=6&devModel=Chrome&uuid={uuid}&extra=2×tamp={ts}&shareId={shareId}&type=0&offset=1
|
||||
// &limit=60
|
||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome&extra" +
|
||||
"=2&shareId={shareId}&type=0&offset=1&limit=60";
|
||||
|
||||
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "file/redirect?downloadId={fidEncode}&enable=1" +
|
||||
"&devType=6&uuid={uuid}×tamp={ts}&auth={auth}&shareId={dataKey}";
|
||||
// https://api.feijipan.com/ws/file/redirect?downloadId={fidEncode}&enable=1&devType=6&uuid={uuid}×tamp={ts}&auth={auth}&shareId={dataKey}
|
||||
"&devType=6&uuid={uuid}×tamp={ts}&auth={auth}";
|
||||
|
||||
|
||||
private static final String VIP_REQUEST_URL = API_URL_PREFIX + "/buy/vip/list?devType=6&devModel=Chrome&uuid" +
|
||||
"={uuid}&extra=2×tamp={ts}";
|
||||
// https://api.feijipan.com/ws/buy/vip/list?devType=6&devModel=Chrome&uuid=WQAl5yBy1naGudJEILBvE&extra=2×tamp=E2C53155F6D09417A27981561134CB73
|
||||
|
||||
public FjTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public FjTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
String dataKey;
|
||||
if (key.startsWith(SHARE_URL_PREFIX2)) {
|
||||
dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX2, key);
|
||||
} else {
|
||||
dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
}
|
||||
|
||||
// 240530 此处shareId又改为了原始的shareId
|
||||
String shareId = dataKey; // String.valueOf(AESUtils.idEncrypt(dataKey));
|
||||
long nowTs = System.currentTimeMillis();
|
||||
String tsEncode = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
||||
String uuid = UUIDUtil.fjUuid(); // 也可以使用 UUID.randomUUID().toString()
|
||||
WebClient client = clientNoRedirects;
|
||||
String shareId = String.valueOf(AESUtils.idEncrypt(dataKey));
|
||||
|
||||
// 24.5.12 飞机盘 规则修改 需要固定UUID先请求会员接口, 再请求后续接口
|
||||
client.postAbs(UriTemplate.of(VIP_REQUEST_URL))
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
.send().onSuccess(r0 -> { // 忽略res
|
||||
// 第一次请求 获取文件信息
|
||||
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
||||
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("shareId", shareId).send().onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
if (resJson.getInteger("code") != 200) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
return;
|
||||
}
|
||||
if (resJson.getJsonArray("list").size() == 0) {
|
||||
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
|
||||
return;
|
||||
}
|
||||
// 文件Id
|
||||
JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0);
|
||||
String fileId = fileInfo.getString("fileIds");
|
||||
String userId = fileInfo.getString("userId");
|
||||
// 其他参数
|
||||
long nowTs = System.currentTimeMillis();
|
||||
String tsEncode = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String fidEncode = AESUtils.encrypt2Hex(fileId + "|");
|
||||
String auth = AESUtils.encrypt2Hex(fileId + "|" + nowTs);
|
||||
|
||||
// long nowTs0 = System.currentTimeMillis();
|
||||
String tsEncode0 = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
||||
// 第一次请求 获取文件信息
|
||||
// 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(FIRST_REQUEST_URL))
|
||||
.setTemplateParam("shareId", shareId)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode0)
|
||||
.send().onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
if (resJson.getInteger("code") != 200) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
return;
|
||||
}
|
||||
if (resJson.getJsonArray("list").size() == 0) {
|
||||
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
|
||||
return;
|
||||
}
|
||||
// 文件Id
|
||||
JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0);
|
||||
String fileId = fileInfo.getString("fileIds");
|
||||
String userId = fileInfo.getString("userId");
|
||||
// 其他参数
|
||||
long nowTs2 = System.currentTimeMillis();
|
||||
String tsEncode2 = AESUtils.encrypt2Hex(Long.toString(nowTs2));
|
||||
String fidEncode = AESUtils.encrypt2Hex(fileId + "|" + userId);
|
||||
String auth = AESUtils.encrypt2Hex(fileId + "|" + nowTs2);
|
||||
|
||||
MultiMap headers0 = MultiMap.caseInsensitiveMultiMap();
|
||||
headers0.set("referer", REFERER_URL);
|
||||
// 第二次请求
|
||||
HttpRequest<Buffer> httpRequest =
|
||||
clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.putHeaders(headers0)
|
||||
.setTemplateParam("fidEncode", fidEncode)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode2)
|
||||
.setTemplateParam("auth", auth)
|
||||
.setTemplateParam("dataKey", dataKey);
|
||||
System.out.println(httpRequest.toString());
|
||||
httpRequest.send().onSuccess(res2 -> {
|
||||
MultiMap headers = res2.headers();
|
||||
if (!headers.contains("Location")) {
|
||||
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers());
|
||||
return;
|
||||
}
|
||||
promise.complete(headers.get("Location"));
|
||||
}).onFailure(handleFail(SECOND_REQUEST_URL));
|
||||
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
|
||||
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
MultiMap headers0 = MultiMap.caseInsensitiveMultiMap();
|
||||
headers0.set("referer", REFERER_URL);
|
||||
// 第二次请求
|
||||
client.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.putHeaders(headers0)
|
||||
.setTemplateParam("fidEncode", fidEncode)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
.setTemplateParam("auth", auth).send().onSuccess(res2 -> {
|
||||
MultiMap headers = res2.headers();
|
||||
if (!headers.contains("Location")) {
|
||||
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers());
|
||||
return;
|
||||
}
|
||||
promise.complete(headers.get("Location"));
|
||||
}).onFailure(handleFail(SECOND_REQUEST_URL));
|
||||
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.AESUtils;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -26,19 +27,19 @@ public class IzTool extends PanBase implements IPanTool {
|
||||
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "file/redirect?downloadId={fidEncode}&enable=1" +
|
||||
"&devType=6&uuid={uuid}×tamp={ts}&auth={auth}";
|
||||
|
||||
public IzTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public IzTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
String dataKey = shareLinkInfo.getShareKey();
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
|
||||
// 24.5.12 ilanzou改规则无需计算shareId
|
||||
// String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey));
|
||||
WebClient client = clientNoRedirects;
|
||||
String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey));
|
||||
|
||||
// 第一次请求 获取文件信息
|
||||
// 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(FIRST_REQUEST_URL)).setTemplateParam("shareId", dataKey).send().onSuccess(res -> {
|
||||
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("shareId", shareId).send().onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
if (resJson.getInteger("code") != 200) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
@@ -60,7 +61,7 @@ public class IzTool extends PanBase implements IPanTool {
|
||||
String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId);
|
||||
String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs);
|
||||
// 第二次请求
|
||||
clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
client.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.setTemplateParam("fidEncode", fidEncode)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
@@ -13,15 +13,16 @@ import java.util.UUID;
|
||||
* <a href="https://lecloud.lenovo.com/">联想乐云</a>
|
||||
*/
|
||||
public class LeTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://lecloud.lenovo.com/share/";
|
||||
private static final String API_URL_PREFIX = "https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/";
|
||||
|
||||
public LeTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public LeTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
// {"shareId":"xxx","password":"xxx","directoryId":"-1"}
|
||||
String apiUrl1 = API_URL_PREFIX + "shareInfo";
|
||||
client.postAbs(apiUrl1)
|
||||
@@ -46,7 +47,7 @@ public class LeTool extends PanBase implements IPanTool {
|
||||
JsonObject fileInfoJson = files.getJsonObject(0);
|
||||
if (fileInfoJson != null) {
|
||||
// TODO 文件大小fileSize和文件名fileName
|
||||
String fileId = fileInfoJson.getString("fileId");
|
||||
Long fileId = fileInfoJson.getLong("fileId");
|
||||
// 根据文件ID获取跳转链接
|
||||
getDownURL(dataKey, fileId);
|
||||
}
|
||||
@@ -60,7 +61,7 @@ public class LeTool extends PanBase implements IPanTool {
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private void getDownURL(String key, String fileId) {
|
||||
private void getDownURL(String key, Long fileId) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
JsonArray fileIds = JsonArray.of(fileId);
|
||||
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.JsExecUtils;
|
||||
@@ -24,15 +23,15 @@ public class LzTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://wwwa.lanzoux.com";
|
||||
|
||||
public static final String LINK_KEY = "lanzou";
|
||||
|
||||
public LzTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public LzTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Future<String> parse() {
|
||||
String sUrl = shareLinkInfo.getStandardUrl();
|
||||
String pwd = shareLinkInfo.getSharePassword();
|
||||
String sUrl = key.startsWith("https://") ? key : SHARE_URL_PREFIX + "/" + key;
|
||||
|
||||
WebClient client = clientNoRedirects;
|
||||
client.getAbs(sUrl).send().onSuccess(res -> {
|
||||
@@ -51,37 +50,34 @@ public class LzTool extends PanBase implements IPanTool {
|
||||
}
|
||||
|
||||
jsText = jsText.replace("document.getElementById('pwd').value", "\"" + pwd + "\"");
|
||||
int i = jsText.indexOf("document.getElementById('rpt')");
|
||||
if (i > 0) {
|
||||
jsText = jsText.substring(0, i);
|
||||
}
|
||||
jsText = jsText.substring(0, jsText.indexOf("document.getElementById('rpt')"));
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "down_p");
|
||||
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 没有密码
|
||||
String iframePath = matcher.group(1);
|
||||
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
|
||||
String html2 = res2.bodyAsString();
|
||||
|
||||
// 去TMD正则
|
||||
// Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
|
||||
String jsText = getJsText(html2);
|
||||
if (jsText == null) {
|
||||
fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": js脚本匹配失败, 可能分享已失效");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, null);
|
||||
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
}
|
||||
}).onFailure(handleFail(SHARE_URL_PREFIX));
|
||||
return;
|
||||
}
|
||||
String iframePath = matcher.group(1);
|
||||
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
|
||||
String html2 = res2.bodyAsString();
|
||||
|
||||
// 去TMD正则
|
||||
// Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
|
||||
String jsText = getJsText(html2);
|
||||
if (jsText == null) {
|
||||
fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": js脚本匹配失败, 可能分享已失效");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, null);
|
||||
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
}
|
||||
}).onFailure(handleFail(SHARE_URL_PREFIX));
|
||||
}).onFailure(handleFail(sUrl));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@@ -1,48 +1,43 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.util.StringUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.StringUtils;
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
|
||||
/**
|
||||
* <a href="https://wx.mail.qq.com/">QQ邮箱</a>
|
||||
*/
|
||||
public class QQTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String REDIRECT_URL_TEMP = "https://iwx.mail.qq.com/ftn/download?func=4&key={key}&code={code}";
|
||||
public static final String SHARE_URL_PREFIX = "wx.mail.qq.com/ftn/download?";
|
||||
|
||||
public QQTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public QQTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Future<String> parse() {
|
||||
// QQ mail 直接替换为302链接 无需请求
|
||||
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(shareLinkInfo.getShareUrl(), StandardCharsets.UTF_8);
|
||||
Map<String, List<String>> prms = queryStringDecoder.parameters();
|
||||
if (prms.containsKey("key") && prms.containsKey("code") && prms.containsKey("func")) {
|
||||
log.info(prms.get("func").get(0));
|
||||
promise.complete(REDIRECT_URL_TEMP.replace("{key}",
|
||||
prms.get("key").get(0)).replace("{code}", prms.get("code").get(0)));
|
||||
} else {
|
||||
fail("key 不合法");
|
||||
|
||||
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 {
|
||||
throw new UnsupportedOperationException("未知分享类型");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 通过请求URL获取文件信息和直链 暂时不需要
|
||||
// getFileInfo(key);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private void getFileInfo(String key) {
|
||||
// 设置基础HTTP头部
|
||||
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
|
||||
"like " +
|
||||
@@ -55,7 +50,7 @@ public class QQTool extends PanBase implements IPanTool {
|
||||
headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile");
|
||||
|
||||
// 获取下载中转站页面
|
||||
client.getAbs(key).putHeaders(headers).send().onSuccess(res -> {
|
||||
httpClient.getAbs(this.key).putHeaders(headers).send().onSuccess(res -> {
|
||||
if (res.statusCode() == 200) {
|
||||
String html = res.bodyAsString();
|
||||
|
||||
@@ -66,10 +61,10 @@ public class QQTool extends PanBase implements IPanTool {
|
||||
|
||||
if (filename != null && filesize != null && fileurl != null) {
|
||||
// 设置所需HTTP头部
|
||||
headers.set("Referer", "https://" + StringUtils.StringCutNot(key, "https://", "/") + "/");
|
||||
headers.set("Referer", "https://" + StringUtils.StringCutNot(this.key, "https://", "/") + "/");
|
||||
headers.set("Host", StringUtils.StringCutNot(fileurl, "https://", "/"));
|
||||
res.headers().forEach((k, v) -> {
|
||||
if (k.equalsIgnoreCase("set-cookie")) {
|
||||
if (k.toLowerCase().equals("set-cookie")) {
|
||||
headers.set("Cookie", "mail5k=" + StringUtils.StringCutNot(v, "mail5k=", ";") + ";");
|
||||
}
|
||||
});
|
||||
@@ -87,7 +82,9 @@ public class QQTool extends PanBase implements IPanTool {
|
||||
} else {
|
||||
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
||||
}
|
||||
}).onFailure(this.handleFail(key));
|
||||
}).onFailure(this.handleFail(this.key));
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import io.vertx.core.Future;
|
||||
@@ -10,14 +9,11 @@ import java.util.stream.IntStream;
|
||||
|
||||
public class QkTool extends PanBase implements IPanTool {
|
||||
|
||||
public QkTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public QkTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
final String key = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
|
||||
promise.complete("https://lz.qaiu.top");
|
||||
IntStream.range(0, 1000).forEach(num -> {
|
||||
clientNoRedirects.getAbs(key).send()
|
||||
@@ -37,6 +33,14 @@ public class QkTool extends PanBase implements IPanTool {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
new QkTool("https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12298704&dk" +
|
||||
"=19ab590770399d4438ea885446e27186cc668cdfa559f5fcc063a1ecf78008e5&pk" +
|
||||
"=ef45aa4d25c1dcecb631b3394f51539d59cb35c6a40c3911df8ba431ba2a3244&pc=true&ot=ali&ob=sync-cloud-disk" +
|
||||
"&ok=649593714557087744.dex&fn=classes" +
|
||||
".dex&ds=8909208&dc=1&bi=asdddsad&ri=&ts=1701235051759&sn" +
|
||||
"=13dc33749bd9cc108009fa505b3ecca9f358d70874352858475956ba4240e4c3", "")
|
||||
.parse().onSuccess((res) -> {
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
82
parser/src/main/java/cn/qaiu/parser/impl/UcTool.java
Normal file
82
parser/src/main/java/cn/qaiu/parser/impl/UcTool.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
/**
|
||||
* UC网盘解析
|
||||
*/
|
||||
public class UcTool extends PanBase implements IPanTool {
|
||||
private static final String API_URL_PREFIX = "https://pc-api.uc.cn/1/clouddrive/";
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://fast.uc.cn/s/";
|
||||
|
||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "share/sharepage/token?entry=ft&fr=pc&pr" +
|
||||
"=UCBrowser";
|
||||
|
||||
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "transfer_share/detail?pwd_id={pwd_id}&passcode" +
|
||||
"={passcode}&stoken={stoken}";
|
||||
|
||||
private static final String THIRD_REQUEST_URL = API_URL_PREFIX + "file/download?entry=ft&fr=pc&pr=UCBrowser";
|
||||
|
||||
public UcTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
var dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
var passcode = (pwd == null) ? "" : pwd;
|
||||
var jsonObject = JsonObject.of("share_for_transfer", true);
|
||||
jsonObject.put("pwd_id", dataKey);
|
||||
jsonObject.put("passcode", passcode);
|
||||
// 第一次请求 获取文件信息
|
||||
client.postAbs(FIRST_REQUEST_URL).sendJsonObject(jsonObject).onSuccess(res -> {
|
||||
log.debug("第一阶段 {}", res.body());
|
||||
var resJson = res.bodyAsJsonObject();
|
||||
if (resJson.getInteger("code") != 0) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
return;
|
||||
}
|
||||
var stoken = resJson.getJsonObject("data").getString("stoken");
|
||||
// 第二次请求
|
||||
client.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.setTemplateParam("pwd_id", dataKey)
|
||||
.setTemplateParam("passcode", passcode)
|
||||
.setTemplateParam("stoken", stoken)
|
||||
.send().onSuccess(res2 -> {
|
||||
log.debug("第二阶段 {}", res2.body());
|
||||
JsonObject resJson2 = res2.bodyAsJsonObject();
|
||||
if (resJson2.getInteger("code") != 0) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson2);
|
||||
return;
|
||||
}
|
||||
// 文件信息
|
||||
var info = resJson2.getJsonObject("data").getJsonArray("list").getJsonObject(0);
|
||||
// 第二次请求
|
||||
var bodyJson = JsonObject.of()
|
||||
.put("fids", JsonArray.of(info.getString("fid")))
|
||||
.put("pwd_id", dataKey)
|
||||
.put("stoken", stoken)
|
||||
.put("fids_token", JsonArray.of(info.getString("share_fid_token")));
|
||||
client.postAbs(THIRD_REQUEST_URL).sendJsonObject(bodyJson)
|
||||
.onSuccess(res3 -> {
|
||||
log.debug("第三阶段 {}", res3.body());
|
||||
var resJson3 = res3.bodyAsJsonObject();
|
||||
if (resJson3.getInteger("code") != 0) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson2);
|
||||
return;
|
||||
}
|
||||
promise.complete(resJson3.getJsonArray("data").getJsonObject(0).getString("download_url"));
|
||||
}).onFailure(handleFail(THIRD_REQUEST_URL));
|
||||
|
||||
}).onFailure(handleFail(SECOND_REQUEST_URL));
|
||||
}
|
||||
).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
return promise.future();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
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;
|
||||
@@ -9,27 +13,34 @@ import io.vertx.core.json.DecodeException;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* <a href="https://www.wenshushu.cn/">文叔叔</a>
|
||||
*/
|
||||
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(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public WsTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Future<String> parse() {
|
||||
|
||||
WebClient httpClient = this.client;
|
||||
final String key = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
|
||||
// 补全链接
|
||||
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, " +
|
||||
@@ -56,8 +67,8 @@ public class WsTool extends PanBase implements IPanTool {
|
||||
// 获取文件夹信息
|
||||
httpClient.postAbs(SHARE_URL_API + "task/mgrtask").putHeaders(headers)
|
||||
.sendJsonObject(JsonObject.of(
|
||||
"tid", key,
|
||||
"password", pwd
|
||||
"tid", StringUtils.StringCutNot(key, this.key.startsWith(SHARE_URL_PREFIX) ? SHARE_URL_PREFIX : SHARE_URL_PREFIX2),
|
||||
"password", ""
|
||||
)).onSuccess(res2 -> {
|
||||
|
||||
if (res2.statusCode() == 200) {
|
||||
@@ -118,9 +129,16 @@ public class WsTool extends PanBase implements IPanTool {
|
||||
// 调试输出文件直链
|
||||
System.out.println("文件直链: " + fileurl);
|
||||
|
||||
if (!fileurl.equals("")) {
|
||||
promise.complete(URLDecoder.decode(fileurl, StandardCharsets.UTF_8));
|
||||
} else {
|
||||
if (!fileurl.equals(""))
|
||||
{
|
||||
try {
|
||||
promise.complete(URLDecoder.decode(fileurl, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
promise.complete(fileurl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.fail("文件已失效");
|
||||
}
|
||||
|
||||
@@ -149,7 +167,7 @@ public class WsTool extends PanBase implements IPanTool {
|
||||
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
||||
}
|
||||
|
||||
}).onFailure(this.handleFail(key));
|
||||
}).onFailure(this.handleFail(this.key));
|
||||
|
||||
} catch (DecodeException | NullPointerException e) {
|
||||
this.fail("token获取失败,可能是分享链接的方式已更新");
|
||||
@@ -158,7 +176,7 @@ public class WsTool extends PanBase implements IPanTool {
|
||||
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
||||
}
|
||||
|
||||
}).onFailure(this.handleFail(key));
|
||||
}).onFailure(this.handleFail(this.key));
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
@@ -31,14 +30,13 @@ public class YeTool extends PanBase implements IPanTool {
|
||||
"&shareKey={shareKey}&SharePwd={pwd}&ParentFileId=0&Page=1&event=homeListFile&operateType=1";
|
||||
private static final String DOWNLOAD_API_URL = "https://www.123pan.com/a/api/share/download/info?{authK}={authV}";
|
||||
|
||||
public YeTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
public YeTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
|
||||
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("key", dataKey).send().onSuccess(res -> {
|
||||
|
||||
|
||||
@@ -246,7 +246,6 @@ public class AESUtils {
|
||||
return idEncrypt0(str, array, 2, 2);
|
||||
}
|
||||
|
||||
|
||||
// ================================蓝奏优享版Id解密========================================== //
|
||||
public static int idEncryptIz(String str) {
|
||||
// idEncrypt(e) {
|
||||
|
||||
12
parser/src/main/java/cn/qaiu/util/PanExceptionUtils.java
Normal file
12
parser/src/main/java/cn/qaiu/util/PanExceptionUtils.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package cn.qaiu.util;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2023/7/16 1:53
|
||||
*/
|
||||
public class PanExceptionUtils {
|
||||
|
||||
public static RuntimeException fillRunTimeException(String name, String dataKey, Throwable t) {
|
||||
return new RuntimeException(name + ": 请求异常: key = " + dataKey, t.fillInStackTrace());
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package cn.qaiu.util;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/5/13 4:10
|
||||
*/
|
||||
public class UUIDUtil {
|
||||
|
||||
public static String fjUuid() {
|
||||
return generateRandomString(21);
|
||||
}
|
||||
|
||||
public static String generateRandomString(int length) {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] randomBytes = new byte[length];
|
||||
random.nextBytes(randomBytes);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : randomBytes) {
|
||||
int value = b & 0x3F; // 63 in hexadecimal
|
||||
if (value < 36) {
|
||||
sb.append(Integer.toString(value, 36));
|
||||
} else if (value < 62) {
|
||||
sb.append(Character.toUpperCase(Integer.toString(value - 26, 36).charAt(0)));
|
||||
} else if (value > 62) {
|
||||
sb.append("-");
|
||||
} else { // value == 62 or 63
|
||||
sb.append("_");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FCURLParser {// 定义前缀
|
||||
public static final String SHARE_URL_PREFIX0 = "https://v2.fangcloud.com/s";
|
||||
public static final String SHARE_URL_PREFIX = "https://v2.fangcloud.com/sharing/";
|
||||
public static final String SHARE_URL_PREFIX2 = "https://v2.fangcloud.cn/sharing/";
|
||||
|
||||
// 定义正则表达式,适用于所有前缀
|
||||
private static final String SHARING_REGEX = "https://www\\.ecpan\\.cn/web(/%23|/#)?/yunpanProxy\\?path=.*&data=" +
|
||||
"([^&]+)&isShare=1";
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 测试 URL
|
||||
String[] urls = {
|
||||
"https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1",
|
||||
"https://www.ecpan.cn/web/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1",
|
||||
"https://v2.fangcloud.cn/sharing/xyz789"
|
||||
};
|
||||
|
||||
// 编译正则表达式
|
||||
Pattern pattern = Pattern.compile(SHARING_REGEX);
|
||||
|
||||
for (String url : urls) {
|
||||
Matcher matcher = pattern.matcher(url);
|
||||
if (matcher.find()) {
|
||||
System.out.println(matcher.groupCount());
|
||||
String shareKey = matcher.group(matcher.groupCount()); // 捕捉组 3
|
||||
System.out.println("Captured part: " + shareKey);
|
||||
} else {
|
||||
System.out.println("No match found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/8/8 2:39
|
||||
*/
|
||||
public class PanDomainTemplateTest {
|
||||
|
||||
@Test
|
||||
public void normalizeShareLink() {
|
||||
// 准备测试数据
|
||||
String testShareUrl = "https://test.lanzoux.com/s/someShareKey";
|
||||
|
||||
ParserCreate parserCreate = ParserCreate.fromShareUrl(testShareUrl); // 假设使用LZ网盘模板
|
||||
|
||||
// 调用normalizeShareLink方法
|
||||
ShareLinkInfo result = parserCreate.getShareLinkInfo();
|
||||
System.out.println(result);
|
||||
// 断言结果是否符合预期
|
||||
assertNotNull("Result should not be null", result);
|
||||
assertEquals("Share key should match", "someShareKey", result.getShareKey());
|
||||
assertEquals("Standard URL should be generated correctly", parserCreate.getStandardUrlTemplate().replace("{shareKey}", "someShareKey"), result.getStandardUrl());
|
||||
// 可以添加更多的断言来验证其他字段
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromShareUrl() throws InterruptedException {
|
||||
// 准备测试数据
|
||||
String lzUrl = "https://wwn.lanzouy.com/ihLkw1gezutg";
|
||||
String cowUrl = "https://cowtransfer.com/s/9a644fe3e3a748";
|
||||
String ceUrl = "https://pan.huang1111.cn/s/g31PcQ";
|
||||
String wsUrl = "https://f.ws59.cn/f/f25625rv6p6";
|
||||
// ParserCreate.fromShareUrl(wsUrl).createTool()
|
||||
// .parse().onSuccess(System.out::println);
|
||||
// ParserCreate.fromShareUrl(lzUrl).createTool()
|
||||
// .parse().onSuccess(System.out::println);
|
||||
// ParserCreate.fromShareUrl(cowUrl).createTool()
|
||||
// .parse().onSuccess(System.out::println);
|
||||
ParserCreate.fromShareUrl(lzUrl).createTool()
|
||||
.parse().onSuccess(System.out::println);
|
||||
|
||||
// ParserCreate.fromType("lz").shareKey("ihLkw1gezutg")
|
||||
// .createTool().parse().onSuccess(System.out::println);
|
||||
// ParserCreate.LZ.shareKey("ihLkw1gezutg")
|
||||
// .createTool().parse().onSuccess(System.out::println);
|
||||
|
||||
|
||||
// 调用fromShareUrl方法
|
||||
// PanDomainTemplate resultTemplate = ParserCreate.fromShareUrl(testShareUrl);
|
||||
// System.out.println(resultTemplate.normalizeShareLink(testShareUrl));
|
||||
// System.out.println(resultTemplate.shareKey("xxx"));
|
||||
// System.out.println(resultTemplate.createTool("xxx",null).parse()
|
||||
// .onSuccess(System.out::println));
|
||||
// System.out.println(resultTemplate.getDisplayName());
|
||||
// System.out.println(resultTemplate.getStandardUrlTemplate());
|
||||
// System.out.println(resultTemplate.getRegexPattern());
|
||||
//
|
||||
// // 断言结果是否符合预期
|
||||
// assertNotNull("Result should not be null", resultTemplate);
|
||||
// assertEquals("Should return the correct template", ParserCreate.LZ, resultTemplate);
|
||||
// // 可以添加更多的断言来验证正则表达式匹配逻辑
|
||||
// new Scanner(System.in).nextLine();
|
||||
TimeUnit.SECONDS.sleep(5);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public class WebClientExample {
|
||||
WebClient client = WebClient.create(vertx);
|
||||
|
||||
MultipartForm form = new MultipartFormImpl()
|
||||
.attribute("email", "")
|
||||
.attribute("email", "736226400@qq.com")
|
||||
.attribute("password", "");
|
||||
|
||||
client.postAbs("https://cowtransfer.com/api/user/emaillogin")
|
||||
|
||||
35
pom.xml
35
pom.xml
@@ -7,7 +7,7 @@
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>netdisk-fast-download</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>${revision}</version>
|
||||
<version>0.1.7</version>
|
||||
|
||||
<modules>
|
||||
<module>core</module>
|
||||
@@ -17,7 +17,6 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<revision>0.1.8</revision>
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
@@ -28,43 +27,13 @@
|
||||
<slf4j.version>2.0.5</slf4j.version>
|
||||
<vertx.version>4.5.6</vertx.version>
|
||||
<org.reflections.version>0.10.2</org.reflections.version>
|
||||
<lombok.version>1.18.30</lombok.version>
|
||||
<lombok.version>1.18.12</lombok.version>
|
||||
<slf4j.version>2.0.5</slf4j.version>
|
||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||
<commons-beanutils2.version>2.0.0</commons-beanutils2.version>
|
||||
<jackson.version>2.14.2</jackson.version>
|
||||
<logback.version>1.4.12</logback.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-dependencies</artifactId>
|
||||
<version>${vertx.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>core-database</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>parser</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
3098
web-front/package-lock.json
generated
3098
web-front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.4",
|
||||
"axios": "^1.6.0",
|
||||
"core-js": "^3.8.3",
|
||||
"element-ui": "^2.15.12",
|
||||
"vue": "^2.6.14",
|
||||
@@ -26,7 +26,7 @@
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
|
||||
"compression-webpack-plugin": "^6.1.1",
|
||||
"filemanager-webpack-plugin": "8.0.0"
|
||||
"filemanager-webpack-plugin": "2.0.5"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
content="Netdisk fast download 网盘直链解析工具">
|
||||
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
|
||||
<script>LA.init({id:"K8zkCkZMgFA6ShZK",ck:"K8zkCkZMgFA6ShZK"})</script>
|
||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9851170484804006"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<style>
|
||||
.page-loading-wrap {
|
||||
padding: 120px;
|
||||
|
||||
@@ -7,17 +7,19 @@
|
||||
<el-avatar :size="150" :src="avatar"></el-avatar>
|
||||
</div>
|
||||
</div>
|
||||
<h3 style="text-align: center;">NFD网盘直链解析0.1.8_bate(API演示)</h3>
|
||||
<h3 style="text-align: center;">NFD网盘直链解析(API演示)</h3>
|
||||
<div class="typo">
|
||||
<p><strong>项目GitHub </strong><a href="https://github.com/qaiu/netdisk-fast-download" target="_blank"
|
||||
rel="nofollow"><u>netdisk-fast-download</u></a></p>
|
||||
<p><strong>目前支持 </strong>蓝奏云/蓝奏云优享/小飞机盘/123云盘/奶牛快传/移动云云空间/亿方云/文叔叔/QQ邮箱文件中转站</p>
|
||||
<p>已加入缓存机制, 如果遇到解析出的下载链接失效的情况请及时到项目GitHub反馈</p>
|
||||
<p><strong>当前页面修改自开源项目</strong><a href="https://github.com/HurryBy/CloudDiskAnalysis"
|
||||
target="_blank"
|
||||
rel="nofollow"><u>CloudDiskAnalysis</u></a></p>
|
||||
<p><strong>目前支持 </strong>已支持蓝奏云/奶牛快传/移动云云空间/UC网盘(暂时失效)/小飞机盘/亿方云/123云盘</p>
|
||||
<p>
|
||||
<el-button><strong @click="getInfo">刷新API调用统计</strong></el-button>
|
||||
</p>
|
||||
<p>节点1: 回源请求数:{{ node1Info.parserTotal }}, 缓存请求数:{{ node1Info.cacheTotal }}, 总数:{{ node1Info.total }}</p>
|
||||
<!-- <p>节点2: 成功:{{ node2Info.success }},失败:{{ node2Info.fail }},总数:{{ node2Info.total }}</p>-->
|
||||
<p>节点1: 成功:{{ node1Info.success }},失败:{{ node1Info.fail }},总数:{{ node1Info.total }}</p>
|
||||
<p>节点2: 成功:{{ node2Info.success }},失败:{{ node2Info.fail }},总数:{{ node2Info.total }}</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="main" v-loading="isLoading">
|
||||
@@ -147,11 +149,10 @@ export default {
|
||||
message: response.data.msg,
|
||||
type: 'success'
|
||||
})
|
||||
this.downUrl = response.data.data.directLink
|
||||
this.downUrl = response.data.data
|
||||
} else {
|
||||
this.$message.error(response.data.msg)
|
||||
}
|
||||
this.getInfo()
|
||||
},
|
||||
error => {
|
||||
this.isLoading = false
|
||||
@@ -173,12 +174,12 @@ export default {
|
||||
this.node1Info = response.data.data
|
||||
}
|
||||
})
|
||||
// axios.get('/n2/statisticsInfo').then(
|
||||
// response => {
|
||||
// if (response.data.success) {
|
||||
// this.node2Info = response.data.data
|
||||
// }
|
||||
// })
|
||||
axios.get('/n2/statisticsInfo').then(
|
||||
response => {
|
||||
if (response.data.success) {
|
||||
this.node2Info = response.data.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -40,17 +40,17 @@ module.exports = {
|
||||
test: /\.js$|\.html$|\.css/, // 匹配文件
|
||||
threshold: 10240 // 对超过10k文件压缩
|
||||
}),
|
||||
// new FileManagerPlugin({ //初始化 filemanager-webpack-plugin 插件实例
|
||||
// onEnd: {
|
||||
// mkdir: ['./nfd-front'],
|
||||
// delete: [ //首先需要删除项目根目录下的dist.zip
|
||||
// './nfd-front.zip',
|
||||
// ],
|
||||
// archive: [ //然后我们选择dist文件夹将之打包成dist.zip并放在根目录
|
||||
// {source: './nfd-front', destination: './nfd-front.zip'},
|
||||
// ]
|
||||
// }
|
||||
// })
|
||||
new FileManagerPlugin({ //初始化 filemanager-webpack-plugin 插件实例
|
||||
onEnd: {
|
||||
mkdir: ['./nfd-front'],
|
||||
delete: [ //首先需要删除项目根目录下的dist.zip
|
||||
'./nfd-front.zip',
|
||||
],
|
||||
archive: [ //然后我们选择dist文件夹将之打包成dist.zip并放在根目录
|
||||
{source: './nfd-front', destination: './nfd-front.zip'},
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
6771
web-front/yarn.lock
6771
web-front/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -5,9 +5,10 @@
|
||||
<parent>
|
||||
<artifactId>netdisk-fast-download</artifactId>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>0.1.7</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<version>0.1.7</version>
|
||||
<artifactId>web-service</artifactId>
|
||||
|
||||
<properties>
|
||||
@@ -21,19 +22,12 @@
|
||||
<dependency>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>core-database</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>parser</artifactId>
|
||||
<version>1.0.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<version>1.18.26</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!--logback日志实现-->
|
||||
@@ -53,6 +47,16 @@
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>core-database</artifactId>
|
||||
<version>0.1.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.qaiu</groupId>
|
||||
<artifactId>parser</artifactId>
|
||||
<version>0.1.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
||||
@@ -2,7 +2,6 @@ package cn.qaiu.lz;
|
||||
|
||||
import cn.qaiu.WebClientVertxInit;
|
||||
import cn.qaiu.db.pool.JDBCPoolInit;
|
||||
import cn.qaiu.lz.common.cache.CacheConfigLoader;
|
||||
import cn.qaiu.vx.core.Deploy;
|
||||
import cn.qaiu.vx.core.util.ConfigConstant;
|
||||
import cn.qaiu.vx.core.util.VertxHolder;
|
||||
@@ -24,20 +23,17 @@ public class AppMain {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化数据库/缓存等
|
||||
* 初始化数据库
|
||||
*
|
||||
* @param jsonObject 配置
|
||||
*/
|
||||
private static void exec(JsonObject jsonObject) {
|
||||
WebClientVertxInit.init(VertxHolder.getVertxInstance());
|
||||
DatabindCodec.mapper().registerModule(new JavaTimeModule());
|
||||
// 数据库
|
||||
if (jsonObject.getJsonObject(ConfigConstant.SERVER).getBoolean("enableDatabase")) {
|
||||
JDBCPoolInit.builder().config(jsonObject.getJsonObject("dataSource")).build().initPool();
|
||||
}
|
||||
// 缓存
|
||||
if (jsonObject.containsKey(ConfigConstant.CACHE)) {
|
||||
CacheConfigLoader.init(jsonObject.getJsonObject(ConfigConstant.CACHE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package cn.qaiu.lz.common.cache;
|
||||
|
||||
import cn.qaiu.parser.PanDomainTemplate;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/12 7:38
|
||||
*/
|
||||
public class CacheConfigLoader {
|
||||
private static final Map<String, Integer> CONFIGS = new HashMap<>();
|
||||
public static String TYPE;
|
||||
public static Integer DEFAULT_DURATION;
|
||||
|
||||
public static void init(JsonObject config) {
|
||||
TYPE = config.getString("type");
|
||||
Integer defaultDuration = config.getInteger("defaultDuration");
|
||||
DEFAULT_DURATION = defaultDuration == null ? 60 : defaultDuration;
|
||||
config.getJsonObject("duration").getMap().forEach((k,v) -> {
|
||||
if (v == null) {
|
||||
CONFIGS.put(k, DEFAULT_DURATION);
|
||||
} else {
|
||||
CONFIGS.put(k, (Integer) v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Integer getDuration(PanDomainTemplate pdt) {
|
||||
return CONFIGS.get(pdt.name().toLowerCase());
|
||||
}
|
||||
public static Integer getDuration(String type) {
|
||||
return CONFIGS.get(type.toLowerCase());
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package cn.qaiu.lz.common.cache;
|
||||
|
||||
import cn.qaiu.db.pool.JDBCPoolInit;
|
||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.jdbcclient.JDBCPool;
|
||||
import io.vertx.sqlclient.Row;
|
||||
import io.vertx.sqlclient.templates.SqlTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CacheManager {
|
||||
private final JDBCPool jdbcPool = JDBCPoolInit.instance().getPool();
|
||||
|
||||
|
||||
public Future<CacheLinkInfo> get(String cacheKey) {
|
||||
String sql = "SELECT share_key as shareKey, direct_link as directLink, expiration FROM cache_link_info WHERE share_key = #{share_key}";
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("share_key", cacheKey);
|
||||
Promise<CacheLinkInfo> promise = Promise.promise();
|
||||
SqlTemplate.forQuery(jdbcPool, sql)
|
||||
.mapTo(CacheLinkInfo.class)
|
||||
.execute(params)
|
||||
.onSuccess(rows->{
|
||||
CacheLinkInfo cacheHit;
|
||||
if (rows.size() > 0) {
|
||||
cacheHit = rows.iterator().next();
|
||||
cacheHit.setCacheHit(true);
|
||||
} else {
|
||||
cacheHit = new CacheLinkInfo(JsonObject.of("cacheHit", false, "shareKey", cacheKey));
|
||||
}
|
||||
promise.complete(cacheHit);
|
||||
}).onFailure(Throwable::printStackTrace);
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
|
||||
// 插入或更新缓存数据
|
||||
public Future<Void> cacheShareLink(CacheLinkInfo cacheLinkInfo) {
|
||||
String sql = "MERGE INTO cache_link_info (share_key, direct_link, expiration) " +
|
||||
"KEY (share_key) " +
|
||||
"VALUES (#{shareKey}, #{directLink}, #{expiration})";
|
||||
|
||||
// 直接传递 CacheLinkInfo 实体类
|
||||
return SqlTemplate.forUpdate(jdbcPool, sql)
|
||||
.mapFrom(CacheLinkInfo.class) // 将实体类映射为 Tuple 参数
|
||||
.execute(cacheLinkInfo)
|
||||
.mapEmpty();
|
||||
}
|
||||
|
||||
// 统计网盘厂商API解析次数
|
||||
public Future<Integer> updateTotalByCached(String shareKey) {
|
||||
Promise<Integer> promise = Promise.promise();
|
||||
String sql = """
|
||||
MERGE INTO `api_statistics_info` (`pan_type`, `share_key`, `cache_hit_total`, `update_ts`)
|
||||
KEY (`share_key`)
|
||||
VALUES (#{panType}, #{shareKey}, #{total}, #{ts})
|
||||
""";
|
||||
|
||||
getShareKeyTotal(shareKey, "cache_hit_total").onSuccess(total -> {
|
||||
Integer newTotal = (total == null ? 0 : total) + 1;
|
||||
SqlTemplate.forUpdate(jdbcPool, sql)
|
||||
.execute(new HashMap<>() {{
|
||||
put("panType", getShareType(shareKey));
|
||||
put("shareKey", shareKey);
|
||||
put("total", newTotal);
|
||||
put("ts", System.currentTimeMillis());
|
||||
}})
|
||||
.onSuccess(res -> promise.complete(res.rowCount()))
|
||||
.onFailure(Throwable::printStackTrace);
|
||||
});
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
|
||||
private String getShareType(String fullShareKey) {
|
||||
// 将type和shareKey组合成一个字符串作为缓存key
|
||||
return fullShareKey.split(":")[0];
|
||||
}
|
||||
|
||||
// 统计网盘厂商API解析次数
|
||||
public Future<Integer> updateTotalByParser(String shareKey) {
|
||||
Promise<Integer> promise = Promise.promise();
|
||||
String sql = """
|
||||
MERGE INTO `api_statistics_info` (`pan_type`, `share_key`, `api_parser_total`, `update_ts`)
|
||||
KEY (`share_key`)
|
||||
VALUES (#{panType}, #{shareKey}, #{total}, #{ts})
|
||||
""";
|
||||
|
||||
getShareKeyTotal(shareKey, "api_parser_total").onSuccess(total -> {
|
||||
Integer newTotal = (total == null ? 0 : total) + 1;
|
||||
SqlTemplate.forUpdate(jdbcPool, sql)
|
||||
.execute(new HashMap<>() {{
|
||||
put("panType", getShareType(shareKey));
|
||||
put("shareKey", shareKey);
|
||||
put("total", newTotal);
|
||||
put("ts", System.currentTimeMillis());
|
||||
}})
|
||||
.onSuccess(res -> promise.complete(res.rowCount()))
|
||||
.onFailure(Throwable::printStackTrace);
|
||||
});
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
public Future<Integer> getShareKeyTotal(String shareKey, String name) {
|
||||
String sql = """
|
||||
select `share_key`, sum({total_name}) sum_num
|
||||
from `api_statistics_info`
|
||||
group by `share_key` having `share_key` = #{shareKey};
|
||||
""".replace("{total_name}", name);
|
||||
Promise<Integer> promise = Promise.promise();
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("shareKey", shareKey);
|
||||
SqlTemplate.forQuery(jdbcPool, sql)
|
||||
.mapTo(Row::toJson)
|
||||
.execute(paramMap)
|
||||
.onSuccess(res -> {
|
||||
Integer total = res.iterator().hasNext() ?
|
||||
res.iterator().next().getInteger("sum_num") : null;
|
||||
promise.complete(total);
|
||||
});
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public class DefaultInterceptor implements BeforeInterceptor {
|
||||
|
||||
@Override
|
||||
public void handle(RoutingContext ctx) {
|
||||
// System.out.println("进入前置拦截器1->" + ctx.request().path());
|
||||
System.out.println("进入前置拦截器1->" + ctx.request().path());
|
||||
doNext(ctx);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.qaiu.db.pool.JDBCPoolInit;
|
||||
import cn.qaiu.lz.common.model.ParserLogInfo;
|
||||
import cn.qaiu.vx.core.annotaions.HandleSortFilter;
|
||||
import cn.qaiu.vx.core.interceptor.AfterInterceptor;
|
||||
import cn.qaiu.vx.core.model.JsonResult;
|
||||
import cn.qaiu.vx.core.util.CommonUtil;
|
||||
import cn.qaiu.vx.core.util.SharedDataUtil;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
@@ -35,9 +36,23 @@ public class LogStatistics implements AfterInterceptor {
|
||||
|
||||
ParserLogInfo parserLogInfo = new ParserLogInfo();
|
||||
parserLogInfo.setPath(ctx.request().uri());
|
||||
if (responseData == null) return;
|
||||
if (responseData.containsKey("code") && responseData.getInteger("code") == 500) {
|
||||
log.error("code 500: {} {}", ctx.request().path(), responseData.getString("msg"));
|
||||
if (responseData == null) {
|
||||
String location = ctx.response().headers().get("location");
|
||||
if (location != null) {
|
||||
parserLogInfo.setCode(200);
|
||||
parserLogInfo.setData(location);
|
||||
} else {
|
||||
log.error("location不存在且responseData为空, path={}", ctx.request().path());
|
||||
}
|
||||
insert(parserLogInfo);
|
||||
|
||||
} else if (responseData.containsKey("code")) {
|
||||
JsonResult<?> result = JsonResult.toJsonResult(responseData);
|
||||
parserLogInfo.setCode(result.getCode());
|
||||
parserLogInfo.setData(result.getCode() == 500 ? result.getMsg() : result.getData().toString());
|
||||
insert(parserLogInfo);
|
||||
} else {
|
||||
log.error("未知json日志: {}, path: {}", responseData.encode(), ctx.request().path());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ public class ParserLogInfo {
|
||||
String id = SnowflakeIdWorker.getStringId();
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS", timezone = "GMT+8")
|
||||
Date logTime = new Date();
|
||||
|
||||
@Length(varcharSize = 4096)
|
||||
String path;
|
||||
Integer code;
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package cn.qaiu.lz.common.util;
|
||||
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.http.HttpServerRequest;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 处理URL截断问题,拼接被截断的参数,特殊处理pwd参数。
|
||||
*
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/13
|
||||
*/
|
||||
public class URLParamUtil {
|
||||
|
||||
/**
|
||||
* 解析并处理截断的URL参数
|
||||
*
|
||||
* @param request HttpServerRequest对象
|
||||
* @return 完整的URL字符串
|
||||
*/
|
||||
public static String parserParams(HttpServerRequest request) {
|
||||
|
||||
String url = request.absoluteURI();
|
||||
MultiMap params = request.params();
|
||||
// 处理URL截断的情况,例如: url='https://...&key=...&code=...'
|
||||
if (params.contains("url")) {
|
||||
String encodedUrl = params.get("url");
|
||||
url = handleTruncatedUrl(encodedUrl, params);
|
||||
if (url.endsWith(".html")) {
|
||||
// 123云盘的后缀处理
|
||||
url = url.replace(".html", "");
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理被截断的URL,拼接所有参数,特殊处理pwd参数。
|
||||
*
|
||||
* @param encodedUrl 被截断的url参数
|
||||
* @param params 请求的其他参数
|
||||
* @return 重新拼接后的完整URL
|
||||
*/
|
||||
private static String handleTruncatedUrl(String encodedUrl, MultiMap params) {
|
||||
// 对URL进行解码,以便获取完整的URL
|
||||
String decodedUrl = URLDecoder.decode(encodedUrl, StandardCharsets.UTF_8);
|
||||
|
||||
// 如果URL已经包含查询参数,不需要额外拼接
|
||||
if (params.contains("pwd")) {
|
||||
if (params.size() == 2) {
|
||||
return decodedUrl;
|
||||
}
|
||||
} else {
|
||||
if (params.size() == 1) {
|
||||
return decodedUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// 拼接被截断的URL参数,忽略pwd参数
|
||||
StringBuilder urlBuilder = new StringBuilder(decodedUrl);
|
||||
boolean firstParam = !decodedUrl.contains("?");
|
||||
|
||||
for (String paramName : params.names()) {
|
||||
if (!paramName.equals("url") && !paramName.equals("pwd")) { // 忽略 "url" 和 "pwd" 参数
|
||||
if (firstParam) {
|
||||
urlBuilder.append("?");
|
||||
firstParam = false;
|
||||
} else {
|
||||
urlBuilder.append("&");
|
||||
}
|
||||
urlBuilder.append(paramName).append("=").append(params.get(paramName));
|
||||
}
|
||||
}
|
||||
|
||||
return urlBuilder.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
package cn.qaiu.lz.web.http;
|
||||
|
||||
import cn.qaiu.lz.common.util.URLParamUtil;
|
||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||
import cn.qaiu.lz.web.service.CacheService;
|
||||
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;
|
||||
import cn.qaiu.vx.core.util.AsyncServiceUtil;
|
||||
import cn.qaiu.vx.core.util.ResponseUtil;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
@@ -24,53 +23,58 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@RouteHandler("/")
|
||||
public class ServerApi {
|
||||
|
||||
private final CacheService cacheService = AsyncServiceUtil.getAsyncServiceInstance(CacheService.class);
|
||||
|
||||
@RouteMapping(value = "/parser", method = RouteMethod.GET, order = 4)
|
||||
public Future<Void> parse(HttpServerResponse response, HttpServerRequest request, String pwd) {
|
||||
Promise<Void> promise = Promise.promise();
|
||||
String url = URLParamUtil.parserParams(request);
|
||||
public Future<Void> parse(HttpServerResponse response, HttpServerRequest request, String url, String pwd) {
|
||||
|
||||
cacheService.getCachedByShareUrlAndPwd(url, pwd)
|
||||
.onSuccess(res -> ResponseUtil.redirect(
|
||||
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
||||
.putHeader("nfd-cache-expires", res.getExpires()),
|
||||
res.getDirectLink(), promise))
|
||||
Promise<Void> promise = Promise.promise();
|
||||
if (url.contains(EcTool.SHARE_URL_PREFIX)) {
|
||||
// 默认读取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()));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@RouteMapping(value = "/json/parser", method = RouteMethod.GET, order = 3)
|
||||
public Future<CacheLinkInfo> parseJson(HttpServerRequest request, String pwd) {
|
||||
String url = URLParamUtil.parserParams(request);
|
||||
return cacheService.getCachedByShareUrlAndPwd(url, pwd);
|
||||
public Future<String> parseJson(HttpServerRequest request, String url, String pwd) {
|
||||
if (url.contains(EcTool.SHARE_URL_PREFIX)) {
|
||||
// 默认读取Url参数会被截断手动获取一下其他参数
|
||||
url = EcTool.SHARE_URL_PREFIX + request.getParam("data");
|
||||
}
|
||||
return IPanTool.shareURLPrefixMatching(url, pwd).parse();
|
||||
}
|
||||
|
||||
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET, order = 2)
|
||||
public Future<CacheLinkInfo> parseKeyJson(String type, String key) {
|
||||
String pwd = "";
|
||||
public Future<String> parseKeyJson(String type, String key) {
|
||||
String code = "";
|
||||
if (key.contains("@")) {
|
||||
String[] keys = key.split("@");
|
||||
key = keys[0];
|
||||
pwd = keys[1];
|
||||
code = keys[1];
|
||||
}
|
||||
return cacheService.getCachedByShareKeyAndPwd(type, key, pwd);
|
||||
return IPanTool.typeMatching(type, key, code).parse();
|
||||
}
|
||||
|
||||
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET, order = 1)
|
||||
public Future<Void> parseKey(HttpServerResponse response, String type, String key) {
|
||||
Promise<Void> promise = Promise.promise();
|
||||
String pwd = "";
|
||||
String code = "";
|
||||
if (key.contains("@")) {
|
||||
String[] keys = key.split("@");
|
||||
key = keys[0];
|
||||
pwd = keys[1];
|
||||
code = keys[1];
|
||||
}
|
||||
cacheService.getCachedByShareKeyAndPwd(type, key, pwd)
|
||||
.onSuccess(res -> ResponseUtil.redirect(
|
||||
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
||||
.putHeader("nfd-cache-expires", res.getExpires()),
|
||||
res.getDirectLink(), promise))
|
||||
|
||||
IPanTool.typeMatching(type, key, code).parse()
|
||||
.onSuccess(resUrl -> ResponseUtil.redirect(response, resUrl, promise))
|
||||
.onFailure(t -> promise.fail(t.fillInStackTrace()));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package cn.qaiu.lz.web.model;
|
||||
|
||||
import cn.qaiu.db.ddl.Length;
|
||||
import cn.qaiu.db.ddl.Table;
|
||||
import cn.qaiu.lz.common.ToJson;
|
||||
import io.vertx.codegen.annotations.DataObject;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/11 16:06
|
||||
*/
|
||||
@Table(value = "api_statistics_info", keyFields = "share_key")
|
||||
@Data
|
||||
@DataObject
|
||||
@NoArgsConstructor
|
||||
public class ApiStatisticsInfo implements ToJson {
|
||||
|
||||
/**
|
||||
* pan type 单独拿出来便于统计.
|
||||
*/
|
||||
@Length(varcharSize = 4)
|
||||
private String panType;
|
||||
|
||||
/**
|
||||
* 分享key type:key
|
||||
*/
|
||||
@Length(varcharSize = 4096)
|
||||
private String shareKey;
|
||||
|
||||
/**
|
||||
* 命中缓存次数
|
||||
*/
|
||||
private Integer cacheHitTotal;
|
||||
|
||||
/**
|
||||
* api解析次数
|
||||
*/
|
||||
private Integer apiParserTotal;
|
||||
|
||||
/**
|
||||
* 更新时间戳
|
||||
*/
|
||||
private Long updateTs;
|
||||
|
||||
// 使用 JsonObject 构造
|
||||
public ApiStatisticsInfo(JsonObject json) {
|
||||
if (json.containsKey("panType")) {
|
||||
this.setPanType(json.getString("panType"));
|
||||
}
|
||||
if (json.containsKey("shareKey")) {
|
||||
this.setShareKey(json.getString("shareKey"));
|
||||
}
|
||||
if (json.containsKey("cacheHitTotal")) {
|
||||
this.setCacheHitTotal(json.getInteger("cacheHitTotal"));
|
||||
}
|
||||
if (json.containsKey("apiParserTotal")) {
|
||||
this.setApiParserTotal(json.getInteger("apiParserTotal"));
|
||||
}
|
||||
if (json.containsKey("updateTs")) {
|
||||
this.setUpdateTs(json.getLong("updateTs"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package cn.qaiu.lz.web.model;
|
||||
|
||||
import cn.qaiu.db.ddl.Length;
|
||||
import cn.qaiu.db.ddl.Table;
|
||||
import cn.qaiu.db.ddl.TableGenIgnore;
|
||||
import cn.qaiu.lz.common.ToJson;
|
||||
import io.vertx.codegen.annotations.DataObject;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/11 16:06
|
||||
*/
|
||||
@Table(value = "cache_link_info", keyFields = "share_key")
|
||||
@Data
|
||||
@DataObject
|
||||
@NoArgsConstructor
|
||||
public class CacheLinkInfo implements ToJson {
|
||||
|
||||
/**
|
||||
* 缓存key: type:ShareKey; e.g. lz:xxxx
|
||||
*/
|
||||
@Length(varcharSize = 4096)
|
||||
private String shareKey;
|
||||
|
||||
/**
|
||||
* 解析后的直链
|
||||
*/
|
||||
@Length(varcharSize = 4096)
|
||||
private String directLink;
|
||||
|
||||
/**
|
||||
* 是否命中缓存
|
||||
*/
|
||||
@TableGenIgnore
|
||||
private Boolean cacheHit;
|
||||
|
||||
/**
|
||||
* 到期时间 yyyy-MM-dd hh:mm:ss
|
||||
*/
|
||||
@TableGenIgnore
|
||||
private String expires;
|
||||
|
||||
/**
|
||||
* 有效期
|
||||
*/
|
||||
private Long expiration;
|
||||
|
||||
|
||||
// 使用 JsonObject 构造
|
||||
public CacheLinkInfo(JsonObject json) {
|
||||
if (json.containsKey("shareKey")) {
|
||||
this.setShareKey(json.getString("shareKey"));
|
||||
}
|
||||
if (json.containsKey("directLink")) {
|
||||
this.setDirectLink(json.getString("directLink"));
|
||||
}
|
||||
if (json.containsKey("expires")) {
|
||||
this.setExpires(json.getString("expires"));
|
||||
}
|
||||
if (json.containsKey("expiration")) {
|
||||
this.setExpiration(json.getLong("expiration"));
|
||||
}
|
||||
this.setCacheHit(json.getBoolean("cacheHit", false));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -10,14 +10,14 @@ import lombok.NoArgsConstructor;
|
||||
@NoArgsConstructor
|
||||
@DataObject
|
||||
public class StatisticsInfo implements ToJson {
|
||||
Integer parserTotal;
|
||||
Integer cacheTotal;
|
||||
Integer fail;
|
||||
Integer success;
|
||||
Integer total;
|
||||
|
||||
|
||||
public StatisticsInfo(JsonObject jsonObject) {
|
||||
this.parserTotal = jsonObject.getInteger("parserTotal");
|
||||
this.cacheTotal = jsonObject.getInteger("cacheTotal");
|
||||
this.fail = jsonObject.getInteger("fail");
|
||||
this.success = jsonObject.getInteger("success");
|
||||
this.total = jsonObject.getInteger("total");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package cn.qaiu.lz.web.service;
|
||||
|
||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||
import cn.qaiu.vx.core.base.BaseAsyncService;
|
||||
import io.vertx.codegen.annotations.ProxyGen;
|
||||
import io.vertx.core.Future;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/12 8:26
|
||||
*/
|
||||
@ProxyGen
|
||||
public interface CacheService extends BaseAsyncService {
|
||||
|
||||
Future<CacheLinkInfo> getCachedByShareKeyAndPwd(String type, String shareKey, String pwd);
|
||||
|
||||
Future<CacheLinkInfo> getCachedByShareUrlAndPwd(String shareUrl, String pwd);
|
||||
}
|
||||
@@ -19,5 +19,4 @@ public interface DbService extends BaseAsyncService {
|
||||
Future<JsonObject> sayOk2(String data, UserInfo holder);
|
||||
|
||||
Future<StatisticsInfo> getStatisticsInfo();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.qaiu.lz.web.service;
|
||||
|
||||
import cn.qaiu.vx.core.util.CastUtil;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* JDK代理类工厂
|
||||
*/
|
||||
public class JdkProxyFactory {
|
||||
public static <T> T getProxy(T target) {
|
||||
return CastUtil.cast(Proxy.newProxyInstance(
|
||||
target.getClass().getClassLoader(),
|
||||
target.getClass().getInterfaces(),
|
||||
new ServiceJdkProxy<>(target))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package cn.qaiu.lz.web.service.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.lz.common.cache.CacheConfigLoader;
|
||||
import cn.qaiu.lz.common.cache.CacheManager;
|
||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||
import cn.qaiu.lz.web.service.CacheService;
|
||||
import cn.qaiu.parser.ParserCreate;
|
||||
import cn.qaiu.vx.core.annotaions.Service;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Service
|
||||
public class CacheServiceImpl implements CacheService {
|
||||
|
||||
private final CacheManager cacheManager = new CacheManager();
|
||||
|
||||
private Future<CacheLinkInfo> getAndSaveCachedShareLink(ParserCreate parserCreate) {
|
||||
Promise<CacheLinkInfo> promise = Promise.promise();
|
||||
// 构建组合的缓存key
|
||||
ShareLinkInfo shareLinkInfo = parserCreate.getShareLinkInfo();
|
||||
String cacheKey = generateCacheKey(shareLinkInfo.getType(), shareLinkInfo.getShareKey());
|
||||
// 尝试从缓存中获取
|
||||
cacheManager.get(cacheKey).onSuccess(result -> {
|
||||
// 判断是否已过期
|
||||
// 未命中或者过期
|
||||
if (!result.getCacheHit() || result.getExpiration() < System.currentTimeMillis()) {
|
||||
// parse
|
||||
result.setCacheHit(false);
|
||||
result.setExpiration(0L);
|
||||
parserCreate.createTool().parse().onSuccess(redirectUrl -> {
|
||||
long expires = System.currentTimeMillis() +
|
||||
CacheConfigLoader.getDuration(shareLinkInfo.getType()) * 60 * 1000L;
|
||||
result.setDirectLink(redirectUrl);
|
||||
// result.setExpires(generateDate(expires));
|
||||
promise.complete(result);
|
||||
// 更新缓存
|
||||
// 将直链存储到缓存
|
||||
CacheLinkInfo cacheLinkInfo = new CacheLinkInfo(JsonObject.of(
|
||||
"directLink", redirectUrl,
|
||||
"expiration", expires,
|
||||
"shareKey", cacheKey
|
||||
));
|
||||
cacheManager.cacheShareLink(cacheLinkInfo).onFailure(Throwable::printStackTrace);
|
||||
cacheManager.updateTotalByParser(cacheKey).onFailure(Throwable::printStackTrace);
|
||||
}).onFailure(promise::fail);
|
||||
} else {
|
||||
result.setExpires(generateDate(result.getExpiration()));
|
||||
promise.complete(result);
|
||||
cacheManager.updateTotalByCached(cacheKey).onFailure(Throwable::printStackTrace);
|
||||
}
|
||||
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private String generateCacheKey(String type, String shareKey) {
|
||||
// 将type和shareKey组合成一个字符串作为缓存key
|
||||
return type + ":" + shareKey;
|
||||
}
|
||||
|
||||
private String generateDate(Long ts) {
|
||||
return DateFormatUtils.format(new Date(ts), "yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<CacheLinkInfo> getCachedByShareKeyAndPwd(String type, String shareKey, String pwd) {
|
||||
ParserCreate parserCreate = ParserCreate.fromType(type).shareKey(shareKey).setShareLinkInfoPwd(pwd);
|
||||
return getAndSaveCachedShareLink(parserCreate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<CacheLinkInfo> getCachedByShareUrlAndPwd(String shareUrl, String pwd) {
|
||||
ParserCreate parserCreate = ParserCreate.fromShareUrl(shareUrl).setShareLinkInfoPwd(pwd);
|
||||
return getAndSaveCachedShareLink(parserCreate);
|
||||
}
|
||||
}
|
||||
@@ -48,9 +48,10 @@ public class DbServiceImpl implements DbService {
|
||||
JDBCPool client = JDBCPoolInit.instance().getPool();
|
||||
Promise<StatisticsInfo> promise = Promise.promise();
|
||||
String sql = """
|
||||
select sum(api_parser_total) parserTotal,sum("cache_hit_total") cacheTotal,
|
||||
sum(api_parser_total) + sum("cache_hit_total") total
|
||||
from "api_statistics_info";
|
||||
select COUNT(CASE "code" WHEN 500 THEN "code" END ) "fail",
|
||||
COUNT(CASE "code" WHEN 200 THEN "code" END ) "success",
|
||||
count(1) "total"
|
||||
from "t_parser_log_info"
|
||||
""";
|
||||
SqlTemplate.forQuery(client, sql).mapTo(StatisticsInfo.class).execute(new HashMap<>()).onSuccess(row -> {
|
||||
StatisticsInfo info;
|
||||
|
||||
@@ -35,27 +35,7 @@ custom:
|
||||
# 数据源配置
|
||||
dataSource:
|
||||
provider_class: io.vertx.ext.jdbc.spi.impl.HikariCPDataSourceProvider
|
||||
jdbcUrl: jdbc:h2:file:./db/nfdData;MODE=MySQL;DATABASE_TO_UPPER=FALSE
|
||||
jdbcUrl: jdbc:h2:tcp://127.0.0.1:9095/./db/myData;MODE=MySQL;DATABASE_TO_UPPER=FALSE
|
||||
driverClassName: org.h2.Driver
|
||||
username: root
|
||||
password: '123456'
|
||||
|
||||
# 缓存配置
|
||||
cache:
|
||||
type: h2db
|
||||
# 默认时长: 单位分钟, 实际有效期分钟-1
|
||||
defaultDuration: 59
|
||||
duration:
|
||||
ce:
|
||||
cow:
|
||||
ec:
|
||||
fc:
|
||||
fj:
|
||||
iz:
|
||||
le: 2879
|
||||
lz:
|
||||
qq: 999999
|
||||
ws:
|
||||
ye:
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 要激活的配置: dev--连接本地数据库; prod连接线上数据库
|
||||
active: dev
|
||||
# 版本号
|
||||
version_app: 0.1.8
|
||||
version_app: 0.1.7
|
||||
# 公司名称 -> LOGO版权文字
|
||||
copyright: QAIU
|
||||
|
||||
3
web-service/src/main/resources/conf/dictionaries.json
Normal file
3
web-service/src/main/resources/conf/dictionaries.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
3
web-service/src/main/resources/http-tools/curl.sh
Normal file
3
web-service/src/main/resources/http-tools/curl.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
curl -F "file=@C:\Users\qaiu\Desktop\real\lz-web\web\src\main\resources\logback.xml" -i -XPOST 127.0.0.1:6400/demo/XXX/XXX
|
||||
|
||||
curl -F "file=@C:\Users\qaiu\Desktop\3.csv" -i -XPOST 127.0.0.1:6400/demo/XXX/XXX
|
||||
@@ -1,10 +0,0 @@
|
||||
###超星
|
||||
|
||||
### 直链 Referer
|
||||
https://d0.ananas.chaoxing.com/download/8e8c9baca640d24680d974331390a158?at_=1717958244333&ak_=783925f9ba6eb2d0c711977b777a13e0&ad_=58ffecd38be494bea68f0cda68b18c0a&fn=testgles.c
|
||||
Referer: https://pan-yz.chaoxing.com/external/m/file/1006748113111711744
|
||||
|
||||
|
||||
|
||||
###
|
||||
|
||||
@@ -58,11 +58,4 @@ Content-Type:application/json
|
||||
Origin:https://www.feijipan.com
|
||||
Referer:https://www.feijipan.com/
|
||||
|
||||
{"loginName":"","loginPwd":""}
|
||||
|
||||
|
||||
### 240530 规则修改
|
||||
### https://share.feijipan.com/s/nMtCOXL
|
||||
POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&uuid=WQAl5yBy1naGudJEILBvE&extra=2×tamp=A8ECC3C7C50191ACEB9CB8444FD37624&shareId=nMtCOXL&type=0&offset=1&limit=60
|
||||
|
||||
|
||||
{"loginName":"15687886400","loginPwd":"4458225ff"}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
### PASS 蓝奏云
|
||||
### 蓝奏云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.lanzoux.com/ia2cntg
|
||||
GET http://127.0.0.1:6400/parser?url=https://lanzoux.com/ia2cntg
|
||||
|
||||
### 蓝奏云
|
||||
# @no-redirect
|
||||
@@ -8,19 +8,19 @@ GET http://127.0.0.1:6400/json/parser?url=https://wwsd.lanzoue.com/iLany1e9bbbi
|
||||
|
||||
### 蓝奏云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/json/lz/i6SqHmp1yfc
|
||||
GET http://127.0.0.1:6400/lz/i6SqHmp1yfc
|
||||
### 蓝奏云https://acgtools.lanzoui.com/iUr7Qnu3sxc https://wwn.lanzouy.com/tp/ihLkw1gezutg https://wwn.lanzouy.com/ihLkw1gezutg
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/lz/ihLkw1gezutg
|
||||
|
||||
### 蓝奏云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/json/lz/icBp6qqj82b@QAIU
|
||||
GET http://127.0.0.1:6400/lz/icBp6qqj82b@QAIU
|
||||
|
||||
### 蓝奏云
|
||||
GET http://127.0.0.1:6400/json/lz/ia2cntg
|
||||
|
||||
### PASS 蓝奏云优享
|
||||
### 蓝奏云优享
|
||||
GET http://127.0.0.1:6400/json/iz/lGFndCM
|
||||
|
||||
###
|
||||
@@ -30,14 +30,14 @@ GET http://127.0.0.1:6400/json/parser?url=https://www.ilanzou.com/s/6XwnC4X
|
||||
###
|
||||
GET http://127.0.0.1:6400/json/parser?url=https://www.ilanzou.com/s/zHkna1S
|
||||
|
||||
### 奶牛 https://cowtransfer.com/s/9a644fe3e3a748
|
||||
### 奶牛
|
||||
GET http://127.0.0.1:6400/json/cow/9a644fe3e3a748
|
||||
Referer: https://cowtransfer.com/
|
||||
|
||||
### PASS 奶牛
|
||||
### 奶牛
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/cow/e4f41b51b5da4f
|
||||
|
||||
Referer: https://cowtransfer.com/
|
||||
|
||||
### 奶牛
|
||||
# @no-redirect
|
||||
@@ -47,44 +47,54 @@ GET http://127.0.0.1:6400/parser?url=https://cowtransfer.com/s/9a644fe3e3a748
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://goldrepo.cowtransfer.com/s/026a638795634b
|
||||
|
||||
### 移动云空间 PASS https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
### 移动云空间 https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web//yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
|
||||
### 移动云空间 https://www.ecpan.cn/drive/fileextoverrid.do?chainUrlTemplate=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=aa0cae0164d8885e6d35826b5b2901eckbWJBalM&parentId=-1
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/ec/81027a5c99af5b11ca004966c945cce6W9Bf2
|
||||
|
||||
### https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1 密码 T6O1RC
|
||||
GET http://127.0.0.1:6400/json/ec/4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx@T6O1RC
|
||||
|
||||
###
|
||||
#@no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1&pwd=T6O1RC
|
||||
GET http://127.0.0.1:6400/json/ec/aa0cae0164d8885e6d35826b5b2901eckbWJBalM
|
||||
|
||||
### 小飞机盘 PASS https://share.feijipan.com/s/nMtCOXL
|
||||
|
||||
### UC网盘
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/uc/33197dd53ace4
|
||||
|
||||
### UC网盘
|
||||
GET http://127.0.0.1:6400/json/uc/33197dd53ace4
|
||||
|
||||
### UC网盘
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://fast.uc.cn/s/33197dd53ace4
|
||||
|
||||
|
||||
### 小飞机盘https://share.feijipan.com/s/nMtCOXL
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://share.feijipan.com/s/nMtCOXL
|
||||
|
||||
### 小飞机盘 https://share.feijipan.com/s/laUshYGk
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://share.feijipan.com/s/laUshYGk
|
||||
### 小飞机盘 tIfhRqH
|
||||
### 小飞机盘
|
||||
GET http://127.0.0.1:6400/json/fj/tIfhRqH
|
||||
### 小飞机盘 https://share.feijipan.com/s/7jy0zlv
|
||||
GET http://127.0.0.1:6400/json/fj/7jy0zlv
|
||||
### 小飞机盘 https://share.feijipan.com/s/nMtCOXL
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/fj/nMtCOXL
|
||||
|
||||
### 360亿方云 https://v2.fangcloud.com/sharing/fb54bdf03c66c04334fe3687a3
|
||||
### 小飞机盘
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/fj/tIfhRqH
|
||||
|
||||
### 360亿方云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://v2.fangcloud.com/sharing/fb54bdf03c66c04334fe3687a3
|
||||
|
||||
### 360亿方云
|
||||
GET http://127.0.0.1:6400/json/fc/fb54bdf03c66c04334fe3687a3
|
||||
|
||||
### 360亿方云 PASS
|
||||
### 360亿方云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/fc/e5079007dc31226096628870c7@QAIU
|
||||
|
||||
@@ -99,7 +109,7 @@ GET http://127.0.0.1:6400/json/ye/iaKtVv-6OECd@DcGe
|
||||
### 123
|
||||
GET https://lz.qaiu.top/json/ye/iaKtVv-6OECd@DcGe
|
||||
|
||||
### 123 PASS
|
||||
### 123
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/ye/iaKtVv-qOECd
|
||||
|
||||
@@ -107,9 +117,9 @@ GET http://127.0.0.1:6400/ye/iaKtVv-qOECd
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/ye/Ev1lVv-t3SY3
|
||||
|
||||
### 123 PASS https://www.123pan.com/s/iaKtVv-6OECd.html
|
||||
### 123
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/json/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.html&pwd=DcGe
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.html&pwd=DcGe
|
||||
|
||||
|
||||
### 123
|
||||
@@ -117,37 +127,26 @@ GET http://127.0.0.1:6400/json/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/zF07Vv-WkHWd.html&pwd=bios
|
||||
|
||||
|
||||
### 联想乐云 PASS
|
||||
### 联想乐云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://lecloud.lenovo.com/share/4DANWdRQsHHyiFB4a
|
||||
|
||||
### 联想乐云 PASS
|
||||
### 联想乐云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/le/4DANWdRQsHHyiFB4a
|
||||
|
||||
### 联想乐云 PASS
|
||||
### 联想乐云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/le/2RkKbLP9BrppS9b43@ex2b
|
||||
|
||||
### 联想乐云 PASS
|
||||
### 联想乐云
|
||||
GET http://127.0.0.1:6400/json/le/2RkKbLP9BrppS9b43@ex2b
|
||||
|
||||
### PASS 文叔叔
|
||||
GET http://127.0.0.1:6400/parser?url=https://f.ws59.cn/f/f25625rv6p6
|
||||
###
|
||||
https://f.wss.cc/f/f25625rv6p6
|
||||
|
||||
### TODO Cloudreve
|
||||
### Cloudreve
|
||||
GET http://127.0.0.1:6400/json/ce/https_pan.huang1111.cn_s_wDz5TK
|
||||
|
||||
### Cloudreve https://pan.huang1111.cn/s/g31PcQ
|
||||
GET http://127.0.0.1:6400/parser?url=https://pan.huang1111.cn/s/g31PcQ&pwd=qaiu
|
||||
|
||||
### PASS QQ
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/json/parser?url=https://iwx.mail.qq.com/ftn/download?func=3&key=qweqe&code=8c02cf57&k=asdad
|
||||
|
||||
|
||||
### Cloudreve
|
||||
GET http://127.0.0.1:6400/json/parser?url=https://pan.huang1111.cn/s/g31PcQ&pwd=qaiu
|
||||
|
||||
###
|
||||
GET http://127.0.0.1:6400/v2/statisticsInfo
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package cn.qaiu.web.test;
|
||||
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import org.apache.commons.beanutils2.BeanUtils;
|
||||
import org.apache.commons.beanutils2.ConvertUtils;
|
||||
import org.apache.commons.beanutils2.Converter;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
import org.apache.commons.beanutils.ConvertUtils;
|
||||
import org.apache.commons.beanutils.Converter;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
Reference in New Issue
Block a user