Compare commits

...

37 Commits

Author SHA1 Message Date
QAIU
18dc30c03d 修改123网盘解析规则 2023-07-22 12:34:02 +08:00
QAIU
ff2b7608d2 细节优化 2023-07-21 10:29:21 +08:00
qaiu
5ac5cdced9 Update README.md 2023-07-16 04:26:05 +08:00
qaiu
b6d0a6cd06 修改win服务模板 2023-07-16 03:45:54 +08:00
qaiu
9a20636d2d - 更新版本号: 0.1.5->0.1.6 2023-07-16 03:11:41 +08:00
qaiu
f5abaca5a1 - WebServer: PanTool优化异常处理
- Core: Vertx事件循环线程数调整
2023-07-16 02:47:39 +08:00
qaiu
26ab5f7945 Merge pull request #5 from qaiu/dependabot/maven/com.h2database-h2-2.2.220
Bump h2 from 2.1.214 to 2.2.220
2023-07-10 06:05:17 +08:00
qaiu
c14d8fccfd Merge pull request #4 from qaiu/dependabot/maven/core-database/com.h2database-h2-2.2.220
Bump h2 from 2.1.214 to 2.2.220 in /core-database
2023-07-10 06:04:58 +08:00
dependabot[bot]
fe28050893 Bump h2 from 2.1.214 to 2.2.220
Bumps [h2](https://github.com/h2database/h2database) from 2.1.214 to 2.2.220.
- [Release notes](https://github.com/h2database/h2database/releases)
- [Commits](https://github.com/h2database/h2database/compare/version-2.1.214...version-2.2.220)

---
updated-dependencies:
- dependency-name: com.h2database:h2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-07 22:01:48 +00:00
dependabot[bot]
af9d890f98 Bump h2 from 2.1.214 to 2.2.220 in /core-database
Bumps [h2](https://github.com/h2database/h2database) from 2.1.214 to 2.2.220.
- [Release notes](https://github.com/h2database/h2database/releases)
- [Commits](https://github.com/h2database/h2database/compare/version-2.1.214...version-2.2.220)

---
updated-dependencies:
- dependency-name: com.h2database:h2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-07 22:00:11 +00:00
QAIU
c640b9d9d1 解决一些 warning jdk14对可序列化对象添加@Serial 2023-06-21 16:49:01 +08:00
QAIU
a1dff75bdf Merge remote-tracking branch 'origin/main' 2023-06-21 15:40:42 +08:00
QAIU
f02826339a musetransfer api test 2023-06-21 15:40:24 +08:00
qaiu
656fd672fc Zzz 2023-06-21 02:53:58 +00:00
qaiu
baa2e0ac75 Cow解析重复创建实例VertxHolder.getVertxInstance() 2023-06-21 02:53:24 +00:00
qaiu
3375144af6 cow parse fail return 2023-06-21 02:50:19 +00:00
QAIU
028b75cfd4 add mu pan 2023-06-14 17:47:20 +08:00
QAIU
026cd72a4b Zzz 2023-06-14 16:14:37 +08:00
qaiu
db21eb99f0 Update README.md 2023-06-14 15:48:21 +08:00
qaiu
09aa9a97c2 Update README.md 2023-06-14 15:48:08 +08:00
qaiu
44d18ad273 Update README.md 2023-06-14 15:40:08 +08:00
qaiu
44b82da51d Create LICENSE 2023-06-14 15:23:59 +08:00
QAIU
0f65e6cb2e Zzz 2023-06-14 11:57:58 +08:00
QAIU
407f4fafd9 Zzz 2023-06-14 11:56:33 +08:00
QAIU
66850a72f7 Zzz 2023-06-14 11:53:27 +08:00
QAIU
4acf716868 依赖打包优化 2023-06-14 11:26:33 +08:00
qaiu
2158be14ca 修复Linux运行脚本 2023-06-14 10:30:16 +08:00
QAIU
21d3340f3c Linux部署服务模板修改 2023-06-13 15:11:32 +08:00
qaiu
6972b5e761 Update README.md 2023-06-13 13:58:26 +08:00
QAIU
eac1d62f76 update 0.1.5 2023-06-13 13:25:22 +08:00
QAIU
b88fee9e83 update 0.1.5 2023-06-13 13:23:09 +08:00
QAIU
e075967c14 移动云空间需要注意的地方 2023-06-13 10:53:02 +08:00
qaiu
2c8ee31cb7 Merge pull request #3 from qaiu/dev
Controller 层不支持方法重载: ServerApi
2023-06-13 10:30:36 +08:00
qaiu
1bbb7728d8 Controller 层不支持方法重载: ServerApi 2023-06-13 10:29:23 +08:00
qaiu
7e0d4b3bad 接口重构 2023-06-13 08:22:11 +08:00
qaiu
5f25d32f97 接口重构 2023-06-13 05:43:37 +08:00
QAIU
a890ef54a3 测试相关 2023-06-12 16:41:06 +08:00
47 changed files with 928 additions and 668 deletions

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"java.compile.nullAnalysis.mode": "automatic"
}

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 qaiu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

145
README.md
View File

@@ -1,52 +1,68 @@
# netdisk-fast-download
# 网盘快速下载器--直链解析
[![Java CI with Maven](https://github.com/qaiu/netdisk-fast-download/actions/workflows/maven.yml/badge.svg)](https://github.com/qaiu/netdisk-fast-download/actions/workflows/maven.yml)
[![jdk](https://img.shields.io/badge/jdk-%3E%3D17-blue)](https://www.oracle.com/cn/java/technologies/downloads/)
[![vert.x](https://img.shields.io/badge/vert.x-4.4.1-blue)](https://vertx-china.github.io/)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/qaiu/netdisk-fast-download)](https://github.com/qaiu/netdisk-fast-download/releases/tag/0.1.6-releases)
## 网盘支持情况:
`网盘名称(网盘标识):`
- 蓝奏云 (lz)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- 奶牛快传 (cow)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- 移动云空间 (ec)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- UC网盘 (uc)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- 小飞机网盘 (fj)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- 亿方云 (fc)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- 123云盘 (ye)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- 文叔叔 (ws)
- 夸克网盘 (qk)
- TODO
技术栈:
Jdk17+Vert.x4.4.1+Jsoup
Core模块集成Vert.x实现类spring的注解式路由API
- [蓝奏云 (lz)](https://pc.woozooo.com/)
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- [奶牛快传 (cow)](https://cowtransfer.com/)
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- [移动云空间 (ec)](https://www.ecpan.cn/web)
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- [UC网盘 (uc)](https://fast.uc.cn/)
- [ ] 登录, 上传, 下载, 分享
- [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/)
- [夸克网盘 (qk) 开发中](https://pan.quark.cn/)
**TODO:**
- 登录接口, 文件上传/下载/分享后端接口
- 短地址服务
- 前端界面(正则规划)
**技术栈:**
Jdk17+Vert.x4.4.1
Core模块集成Vert.x实现类似spring的注解式路由API
API接口
```
括号内是可选内容: 表示当带有分享密码时需要加上密码参数
parse接口加上参数pwd=密码;其他接口在分享Key后面加上@密码
网盘标识参考上面网盘支持情况, 括号内是可选内容: 表示当带有分享密码时需要加上密码参数
parser接口可以直接解析分享链接: 加密分享需要加上参数pwd=密码;
其他接口在分享Key后面加上@密码;
1. 解析并自动302跳转 :
http(s)://your_hostname/parser?url=分享链接(&pwd=xxx)
http(s)://your_hostname/网盘标识/分享id(@分享密码)
http(s)://your_host/parser?url=分享链接(&pwd=xxx)
http(s)://your_host/网盘标识/分享key(@分享密码)
2. 获取解析后的直链--JSON格式
http(s)://your_hostname/json/网盘标识/分享id(@分享密码)
3. 有些网盘的加密分享的密码可以忽略: 如移动云空间,小飞机网盘
http(s)://your_host/json/网盘标识/分享key(@分享密码)
3. 特别注意的地方:
- 有些网盘的加密分享的密码可以忽略: 如移动云空间,小飞机网盘
- 移动云空间(ec)使用parser?url= 解析时因为分享链接比较特殊(链接带有参数且含有#符号)所以要么对#进行转义%23要么直接去掉# 或者URL直接是主机名+'/'跟一个data参数
比如 http://your_host/parser?url=https://www.ecpan.cn/web//yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
http://your_host/parser?url=https://www.ecpan.cn/web/%23/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
http://your_host/parser?url=https://www.ecpan.cn/&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
```
IDEA HttpClient示例:
```
# 解析并重定向到直链
### 蓝奏云普通分享
@@ -81,23 +97,66 @@ GET http://127.0.0.1:6400/json/fc/e5079007dc31226096628870c7@QAIU
```
TODO:
解析蓝奏云加密链接
# 网盘对比
| 网盘名称 | 可直接下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 | 登录接口 |
|------------|------------|----------|-----------|---------|------|
|------------|------------------------|----------|-----------|---------|------|
| 蓝奏云 | √ | √ | 不限空间 | 100M | TODO |
| 奶牛快传 | √ | X | 10G | 不限大小 | TODO |
| 移动云空间 | √ | √(密码可忽略) | 5G(个人) | 不限大小 | TODO |
| UC网盘 | √ | √ | 10G | 不限大小 | TODO |
| 小飞机网盘 | √ | √(密码可忽略) | 10G | 不限大小 | TODO |
| 360亿方云 | √(注意有流量限制) | √(密码可忽略) | 100G(须实名) | 不限大小 | TODO |
| 123云盘 | √ | √ | 2T | 不限大小 | TODO |
| 文叔叔(TODO) | √(注意有时间限制) | √ | 10G | 不限大小 | TODO |
| 360亿方云 | √(试用账号有时间限制企业版需要599续费) | √(密码可忽略) | 100G(须实名) | 不限大小 | TODO |
| 123云盘 | √ | √ | 2T | 100G | TODO |
| 文叔叔(TODO) | √(注意有时间限制) | √ | 10G | 5GB | TODO |
| 夸克网盘(TODO) | 需要登录 | √ | 10G | 不限大小 | TODO |
# 打包部署
## 开发和打包
```shell
# 环境要求: Jdk17 + maven;
mvn clean
mvn package
```
打包好的文件位于 web-service/target/netdisk-fast-download-x.x.x-bin.zip
## Linux服务部署
> 注意: netdisk-fast-download.service中的ExecStart的路径改为实际路径
```shell
cd ~
wget -O netdisk-fast-download-0.1.6-bin.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.6-releases/netdisk-fast-download-0.1.6-bin.zip
unzip netdisk-fast-download-*-bin.zip
cd netdisk-fast-download-*-bin
bash service-install.sh
```
服务相关命令:
1、查看服务状态
systemctl status netdisk-fast-download.service
2、控制服务
启动服务
systemctl start netdisk-fast-download.service
重启服务
systemctl restart netdisk-fast-download.service
停止服务
systemctl stop netdisk-fast-download.service
开机启动服务
systemctl enable netdisk-fast-download.servic
停止开机启动
systemctl disable netdisk-fast-download.servic
## Windows服务部署
1. 下载并解压releases版本netdisk-fast-download-0.1.6-bin.zip
2. 进入netdisk-fast-download-0.1.6-bin目录
3. 使用管理员权限运行nfd-service-install.bat
如果不想使用服务运行可以直接运行run.bat
> 注意: 如果jdk环境变量的java版本不是17请修改nfd-service-template.xml中的java命令的路径改为实际路径
## Docker部署
TODO

View File

@@ -7,7 +7,8 @@ Wants=network-online.target
[Service]
Type=simple
# User=USER
ExecStart=/usr/bin/java -server -Xmx128m -jar /root/java/netdisk-fast-download/netdisk-fast-download-0.0.1.jar
# 需要JDK17及以上版本 注意修改为自己的路径
ExecStart=/root/java/jdk-17.0.2/bin/java -server -Xmx128m -jar /root/java/netdisk-fast-download-0.1.6/netdisk-fast-download-0.1.6.jar
ExecStop=/bin/kill -s QUIT $MAINPID
Restart=always
StandOutput=syslog

View File

@@ -0,0 +1,27 @@
::
:: generate service xml file
::
@echo off
pushd %~dp0
set MY_DIR=%~dp0
set MY_DIR=%MY_DIR:~0,-1%
for /f "delims=X" %%i in ('dir /b %MY_DIR%\netdisk-fast-download-*.jar') do (
set LAUNCH_JAR=%MY_DIR%\%%i
)
(for /f "delims=" %%a in (nfd-service-template.xml) do (
set "str=%%a"
setlocal enabledelayedexpansion
set "str=!str:${dd}=%MY_DIR%!"
set "str=!str:${jar}=%LAUNCH_JAR%!"
echo,!str!
endlocal
))>"nfd-service.xml"
sc delete netdisk-fast-download
nfd-service install
sc start netdisk-fast-download
pause

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<service>
<id>netdisk-fast-download</id>
<name>netdisk-fast-download</name>
<description>netdisk fast download service</description>
<executable>java</executable>
<arguments> -server -Xmx128m -jar ${jar} </arguments>
<logpath>${dd}\logs</logpath>
<log mode="roll-by-time">
<pattern>yyyyMMdd</pattern>
</log>
</service>

BIN
bin/nfd-service.exe Normal file

Binary file not shown.

View File

@@ -1,5 +1,10 @@
@echo off && @chcp 65001 > nul
pushd %~dp0
set LIB_DIR=%~dp0
for /f "delims=X" %%i in ('dir /b %LIB_DIR%\netdisk-fast-download-*.jar') do set LAUNCH_JAR=%LIB_DIR%\%%i
for /f "delims=X" %%i in ('dir /b %LIB_DIR%\netdisk-fast-download-*.jar') do (
set LAUNCH_JAR=%LIB_DIR%%%i
)
"%JAVA_HOME%\bin\java.exe" -Xmx512M -Dfile.encoding=utf8 -jar %LAUNCH_JAR% %*
pause

View File

@@ -1,5 +1,6 @@
#!/bin/sh
#!/bin/bash
# set -x
LAUNCH_JAR="netdisk-fast-download-*.jar"
LAUNCH_JAR="netdisk-fast-download-0.1.6.jar"
nohup java -Xmx512M -jar "$LAUNCH_JAR" "$@" >startup.log 2>&1 &
tail -f startup.log

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>netdisk-fast-download</artifactId>
<groupId>cn.qaiu</groupId>
<version>0.1.3</version>
<version>0.1.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,7 +30,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.214</version>
<version>2.2.220</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>netdisk-fast-download</artifactId>
<groupId>cn.qaiu</groupId>
<version>0.1.3</version>
<version>0.1.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<version>1.0.8</version>

View File

@@ -109,7 +109,14 @@ public final class Deploy {
LOGGER.info("配置读取成功");
customConfig = globalConfig.getJsonObject(ConfigConstant.CUSTOM);
var vertxOptions = new VertxOptions(globalConfig.getJsonObject(ConfigConstant.VERTX));
JsonObject vertxConfig = globalConfig.getJsonObject(ConfigConstant.VERTX);
Integer vertxConfigELPS = vertxConfig.getInteger(ConfigConstant.EVENT_LOOP_POOL_SIZE);
var vertxOptions = vertxConfigELPS == 0 ?
new VertxOptions() : new VertxOptions(vertxConfig);
LOGGER.info("vertxConfigEventLoopPoolSize: {}, eventLoopPoolSize: {}, workerPoolSize: {}", vertxConfigELPS,
vertxOptions.getEventLoopPoolSize(),
vertxOptions.getWorkerPoolSize());
var vertx = Vertx.vertx(vertxOptions);
VertxHolder.init(vertx);
//配置保存在共享数据中
@@ -123,6 +130,7 @@ public final class Deploy {
bch.complete("other handle complete");
});
// 部署 路由、异步service、反向代理 服务
var future1 = vertx.deployVerticle(RouterVerticle.class, getWorkDeploymentOptions("Router"));
var future2 = vertx.deployVerticle(ServiceVerticle.class, getWorkDeploymentOptions("Service"));
var future3 = vertx.deployVerticle(ReverseProxyVerticle.class, getWorkDeploymentOptions("proxy"));

View File

@@ -21,6 +21,7 @@ import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
import javassist.CtClass;
@@ -73,12 +74,26 @@ public class RouterHandlerFactory implements BaseHttpApi {
*/
public Router createRouter() {
Router router = Router.router(VertxHolder.getVertxInstance());
// 静态资源
String path = SharedDataUtil.getJsonConfig("server")
.getString("staticResourcePath");
if (!StringUtils.isEmpty(path)) {
// 静态资源
router.route("/*").handler(StaticHandler
.create(path)
.setCachingEnabled(true)
.setDefaultContentEncoding("UTF-8"));
}
router.route().handler(ctx -> {
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(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");
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_HEADERS, "X-PINGOTHER, Origin,Content-Type, Accept, " +
"X-Requested-With, Dev, Authorization, Version, Token");
ctx.response().headers().add(ACCESS_CONTROL_MAX_AGE, "1728000");
ctx.next();
});
@@ -115,6 +130,7 @@ public class RouterHandlerFactory implements BaseHttpApi {
.error("Method Not Allowed", 405)));
router.errorHandler(404, ctx -> ctx.response().setStatusCode(404).setChunked(true)
.end("Internal server error: 404 not found"));
return router;
}
@@ -199,8 +215,8 @@ public class RouterHandlerFactory implements BaseHttpApi {
*/
private String getRouteUrl(String methodName, String mapperValue) {
String routeUrl;
if (mapperValue.startsWith("/:") || "/".equals(mapperValue)) {
routeUrl = (methodName + mapperValue);
if ("/".equals(mapperValue)) {
routeUrl = mapperValue;
} else if (mapperValue.startsWith("/")) {
routeUrl = mapperValue.substring(1);
} else {

View File

@@ -3,6 +3,7 @@ package cn.qaiu.vx.core.model;
import org.apache.commons.lang3.StringUtils;
import java.io.Serial;
import java.io.Serializable;
/**
@@ -13,6 +14,7 @@ import java.io.Serializable;
*/
public class JsonResult<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private static final int SUCCESS_CODE = 200;

View File

@@ -3,6 +3,8 @@ package cn.qaiu.vx.core.util;
public interface ConfigConstant {
String CUSTOM = "custom";
String VERTX = "vertx";
String EVENT_LOOP_POOL_SIZE = "eventLoopPoolSize";
String LOCAL = "local";
String SERVER = "server";
String GLOBAL_CONFIG = "globalConfig";

View File

@@ -7,7 +7,7 @@
<groupId>cn.qaiu</groupId>
<artifactId>netdisk-fast-download</artifactId>
<packaging>pom</packaging>
<version>0.1.3</version>
<version>0.1.6</version>
<modules>
<module>core</module>

View File

@@ -5,10 +5,10 @@
<parent>
<artifactId>netdisk-fast-download</artifactId>
<groupId>cn.qaiu</groupId>
<version>0.1.3</version>
<version>0.1.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<version>0.1.3</version>
<version>0.1.6</version>
<artifactId>web-service</artifactId>
<properties>
@@ -49,11 +49,6 @@
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.4</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
@@ -63,7 +58,7 @@
<dependency>
<groupId>cn.qaiu</groupId>
<artifactId>core-database</artifactId>
<version>0.1.3</version>
<version>0.1.6</version>
</dependency>
@@ -138,8 +133,7 @@
</goals>
<configuration>
<!--打包时排除的依赖作用域-->
<excludeScope>test</excludeScope>
<excludeScope>provided</excludeScope>
<includeScope>runtime</includeScope>
<outputDirectory>
${packageDirectory}/lib/
</outputDirectory>

View File

@@ -10,7 +10,7 @@ import io.vertx.core.json.JsonObject;
* 程序入口
* <br>Create date 2021-05-08 13:00:01
*
* @author qiu
* @author qaiu
*/
public class AppMain {

View File

@@ -0,0 +1,45 @@
package cn.qaiu.lz.common.parser;//package cn.qaiu.lz.common.parser;
import cn.qaiu.lz.common.parser.impl.*;
import io.vertx.core.Future;
public interface IPanTool {
Future<String> parse(String data, String code);
static IPanTool typeMatching(String type) {
return switch (type) {
case "lz" -> new LzTool();
case "cow" -> new CowTool();
case "ec" -> new EcTool();
case "fc" -> new FcTool();
case "uc" -> new UcTool();
case "ye" -> new YeTool();
case "fj" -> new FjTool();
default -> {
throw new IllegalArgumentException("未知分享类型");
}
};
}
static IPanTool shareURLPrefixMatching(String url) {
if (url.startsWith(CowTool.SHARE_URL_PREFIX)) {
return new CowTool();
} else if (url.startsWith(EcTool.SHARE_URL_PREFIX)) {
return new EcTool();
} else if (url.startsWith(FcTool.SHARE_URL_PREFIX)) {
return new FcTool();
} else if (url.startsWith(UcTool.SHARE_URL_PREFIX)) {
return new UcTool();
} else if (url.startsWith(YeTool.SHARE_URL_PREFIX)) {
return new YeTool();
} else if (url.startsWith(FjTool.SHARE_URL_PREFIX)) {
return new FjTool();
} else if (url.contains("lanzou")) {
return new LzTool();
}
throw new IllegalArgumentException("未知分享类型");
}
}

View File

@@ -0,0 +1,15 @@
package cn.qaiu.lz.common.parser;
import cn.qaiu.lz.common.parser.impl.LzTool;
/**
* @author <a href="https://qaiu.top">QAIU</a>
* @date 2023/6/13 4:26
*/
public enum PanType {
LZ("lz"),
COW("cow");
PanType(String type) {
}
}

View File

@@ -0,0 +1,63 @@
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.lz.common.util.PanExceptionUtils;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
/**
* 奶牛快传解析工具
*
* @author <a href="https://qaiu.top">QAIU</a>
* @date 2023/4/21 21:19
*/
@Slf4j
public class CowTool 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 Future<String> parse(String data, String code) {
Promise<String> promise = Promise.promise();
WebClient client = WebClient.create(VertxHolder.getVertxInstance());
String key = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
client.getAbs(API_REQUEST_URL + "?uniqueUrl=" + key).send().onSuccess(res -> {
JsonObject resJson = res.bodyAsJsonObject();
if ("success".equals(resJson.getString("message")) && resJson.containsKey("data")) {
JsonObject dataJson = resJson.getJsonObject("data");
String guid = dataJson.getString("guid");
String fileId = dataJson.getJsonObject("firstFile").getString("id");
String url2 = API_REQUEST_URL + "/download?transferGuid=" + guid + "&fileId=" + fileId;
client.getAbs(url2).send().onSuccess(res2 -> {
JsonObject res2Json = res2.bodyAsJsonObject();
if ("success".equals(res2Json.getString("message")) && res2Json.containsKey("data")) {
JsonObject data2 = res2Json.getJsonObject("data");
String downloadUrl = data2.getString("downloadUrl");
if (StringUtils.isNotEmpty(downloadUrl)) {
log.info("cow parse success: {}", downloadUrl);
promise.complete(downloadUrl);
return;
}
log.error("cow parse fail: {}; downloadUrl is empty", url2);
promise.fail("cow parse fail: " + url2 + "; downloadUrl is empty");
return;
}
log.error("cow parse fail: {}; json: {}", url2, res2Json);
promise.fail("cow parse fail: " + url2 + "; json:" + res2Json);
});
return;
}
log.error("cow parse fail: {}; json: {}", key, resJson);
promise.fail("cow parse fail: " + key + "; json:" + resJson);
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Cow", key, t)));
return promise.future();
}
}

View File

@@ -0,0 +1,63 @@
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.lz.common.util.PanExceptionUtils;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import io.vertx.uritemplate.UriTemplate;
import lombok.extern.slf4j.Slf4j;
/**
* 移动云空间解析
*/
@Slf4j
public class EcTool implements IPanTool {
private static final String FIRST_REQUEST_URL = "https://www.ecpan.cn/drive/fileextoverrid" +
".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 static final String SHARE_URL_PREFIX = "www.ecpan.cn/";
public Future<String> parse(String data, String code) {
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
Promise<String> promise = Promise.promise();
WebClient client = WebClient.create(VertxHolder.getVertxInstance());
// 第一次请求 获取文件信息
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
.getJsonObject("var")
.getJsonObject("chainFileInfo");
if (fileInfo.containsKey("errMesg")) {
promise.fail(new RuntimeException(DOWNLOAD_REQUEST_URL + " 解析失败: "
+ fileInfo.getString("errMesg")) + " key = " + dataKey);
return;
}
JsonObject cloudpFile = fileInfo.getJsonObject("cloudpFile");
JsonArray fileIdList = JsonArray.of(cloudpFile);
// 构造请求JSON {"extCodeFlag":0,"isIp":0}
JsonObject requestBodyJson = JsonObject.of("extCodeFlag", 0, "isIp", 0);
requestBodyJson.put("shareId", Integer.parseInt(fileInfo.getString("shareId"))); // 注意shareId
// 数据类型
requestBodyJson.put("groupId", cloudpFile.getString("groupId"));
requestBodyJson.put("fileIdList", fileInfo.getJsonArray("cloudpFileList"));
// 第二次请求 获取下载链接
client.postAbs(DOWNLOAD_REQUEST_URL).sendJsonObject(requestBodyJson).onSuccess(res2 -> {
JsonObject jsonRes = res2.bodyAsJsonObject();
log.debug("ecPan get download url -> {}", res2.body().toString());
promise.complete(jsonRes.getJsonObject("var").getString("downloadUrl"));
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Ec", dataKey, t)));
}
).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Ec", dataKey, t)));;
return promise.future();
}
}

View File

@@ -1,5 +1,8 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.lz.common.util.PanExceptionUtils;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
@@ -18,16 +21,16 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 亿方云
* 360亿方云
*/
public class FcTool {
public class FcTool implements IPanTool {
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 static Future<String> parse(String data, String code) {
public Future<String> parse(String data, String code) {
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
Promise<String> promise = Promise.promise();
@@ -54,21 +57,21 @@ public class FcTool {
.set("requesttoken", token)
.set("password", code)).onSuccess(res2 -> {
if (res2.statusCode() == 302) {
sClient.getAbs(res2.getHeader("Location")).send().onSuccess(res3 -> {
getDownURL(dataKey, promise, res3, sClient);
});
} else {
promise.fail(SHARE_URL_PREFIX + " 密码跳转后获取重定向失败 \n" + html);
sClient.getAbs(res2.getHeader("Location")).send().onSuccess(res3 ->
getDownURL(dataKey, promise, res3, sClient))
.onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Fc", dataKey, t)));
return;
}
});
promise.fail(SHARE_URL_PREFIX + " 密码跳转后获取重定向失败 \n" + html);
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Fc", dataKey, t)));
return;
}
getDownURL(dataKey, promise, res, sClient);
});
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Fc", dataKey, t)));
return promise.future();
}
private static void getDownURL(String dataKey, Promise<String> promise, HttpResponse<Buffer> res,
private void getDownURL(String dataKey, Promise<String> promise, HttpResponse<Buffer> res,
WebClientSession sClient) {
// 从HTML中找到文件id
String html = res.bodyAsString();
@@ -100,6 +103,6 @@ public class FcTool {
return;
}
promise.complete(resJson.getString("download_url"));
});
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Fc", dataKey, t)));
}
}

View File

@@ -1,5 +1,9 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.util.AESUtils;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.lz.common.util.PanExceptionUtils;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
@@ -16,7 +20,7 @@ import java.util.UUID;
*
* @version V016_230609
*/
public class FjTool {
public class FjTool implements IPanTool {
public static final String SHARE_URL_PREFIX = "https://www.feijix.com/s/";
private static final String API_URL_PREFIX = "https://api.feijipan.com/ws/";
@@ -27,7 +31,7 @@ public class FjTool {
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "file/redirect?downloadId={fidEncode}&enable=1" +
"&devType=6&uuid={uuid}&timestamp={ts}&auth={auth}";
public static Future<String> parse(String data) {
public Future<String> parse(String data, String code) {
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
Promise<String> promise = Promise.promise();
@@ -67,8 +71,8 @@ public class FjTool {
return;
}
promise.complete(headers.get("Location"));
});
});
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Fj", dataKey, t)));
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Fj", dataKey, t)));
return promise.future();
}

View File

@@ -1,10 +1,11 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.util.PanExceptionUtils;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
@@ -18,11 +19,11 @@ import java.util.regex.Pattern;
* @author QAIU
* @version 1.0 update 2021/5/16 10:39
*/
public class LzTool {
public class LzTool implements IPanTool {
public static final String SHARE_URL_PREFIX = "https://wwwa.lanzoui.com";
public static Future<String> parse(String data, String code) {
public Future<String> parse(String data, String code) {
Promise<String> promise = Promise.promise();
String key = data.indexOf('/') > 0 ? data : SHARE_URL_PREFIX + "/" + data;
@@ -31,11 +32,11 @@ public class LzTool {
client.getAbs(key).send().onSuccess(res -> {
String html = res.bodyAsString();
// 匹配iframe
Pattern compile = Pattern.compile("src=\"(/fn\\?[a-zA-Z0-9_+/=]{16,})\"");
Pattern compile = Pattern.compile("src=\"(/fn\\?[a-zA-Z\\d_+/=]{16,})\"");
Matcher matcher = compile.matcher(html);
if (!matcher.find()) {
// 没有Iframe说明是加密分享, 匹配sign通过密码请求下载页面
Pattern compile2 = Pattern.compile("sign=([0-9a-zA-Z_]{16,})");
Pattern compile2 = Pattern.compile("sign=(\\w{16,})");
Matcher matcher2 = compile2.matcher(html);
if (!matcher2.find()) {
promise.fail(key + ": sign正则匹配失败, 可能分享已失效: " + html);
@@ -49,19 +50,19 @@ public class LzTool {
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
String html2 = res2.bodyAsString();
System.out.println(html);
Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'([0-9a-zA-Z_]+)'").matcher(html2);
Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
if (!matcher2.find()) {
promise.fail(SHARE_URL_PREFIX + iframePath + " -> " + key + ": sign正则匹配失败, 可能分享已失效: " + html2);
return;
}
String sign = matcher2.group(1);
getDownURL(promise, code, key, client, sign);
});
});
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Lz", key, t)));
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Lz", key, t)));
return promise.future();
}
private static void getDownURL(Promise<String> promise, String code, String key, WebClient client, String sign) {
private void getDownURL(Promise<String> promise, String code, String key, WebClient client, String sign) {
MultiMap headers = MultiMap.caseInsensitiveMultiMap();
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
"like " +
@@ -82,9 +83,9 @@ public class LzTool {
return;
}
String downUrl = urlJson.getString("dom") + "/file/" + urlJson.getString("url");
client.getAbs(downUrl).putHeaders(headers).send().onSuccess(res3 -> {
promise.complete(res3.headers().get("Location"));
});
});
client.getAbs(downUrl).putHeaders(headers).send()
.onSuccess(res3 -> promise.complete(res3.headers().get("Location")))
.onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Lz", key, t)));
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Lz", key, t)));
}
}

View File

@@ -1,4 +1,4 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.lz.common.parser.impl;
import io.vertx.core.Future;
import io.vertx.core.Promise;

View File

@@ -1,5 +1,8 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.lz.common.util.PanExceptionUtils;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.Promise;
@@ -10,10 +13,10 @@ import io.vertx.uritemplate.UriTemplate;
import lombok.extern.slf4j.Slf4j;
/**
* 移动云空间解析
* UC网盘解析
*/
@Slf4j
public class UcTool {
public class UcTool 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/";
@@ -26,7 +29,7 @@ public class UcTool {
private static final String THIRD_REQUEST_URL = API_URL_PREFIX + "file/download?entry=ft&fr=pc&pr=UCBrowser";
public static Future<String> parse(String data, String code) {
public Future<String> parse(String data, String code) {
var dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
var passcode = (code == null) ? "" : code;
Promise<String> promise = Promise.promise();
@@ -72,13 +75,11 @@ public class UcTool {
return;
}
promise.complete(resJson3.getJsonArray("data").getJsonObject(0).getString("download_url"));
})
.onFailure(t -> promise
.fail(new RuntimeException("解析异常: ", t.fillInStackTrace())));
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Uc", dataKey, t)));
}).onFailure(t -> promise.fail(new RuntimeException("解析异常: ", t.fillInStackTrace())));
}
).onFailure(t -> promise.fail(new RuntimeException("解析异常: key = " + dataKey, t.fillInStackTrace())));
).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Uc", dataKey, t)));
return promise.future();
}
}

View File

@@ -1,11 +1,16 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.util.AESUtils;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.lz.common.util.PanExceptionUtils;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import io.vertx.uritemplate.UriTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.net.MalformedURLException;
@@ -17,14 +22,17 @@ import java.util.regex.Pattern;
/**
* 123网盘
*/
public class YeTool {
@Slf4j
public class YeTool implements IPanTool {
public static final String SHARE_URL_PREFIX = "https://www.123pan.com/s/";
public static final String FIRST_REQUEST_URL = SHARE_URL_PREFIX + "{key}.html";
private static final String GET_FILE_INFO_URL = "https://www.123pan.com/a/api/share/get?limit=100&next=1&orderBy" +
"=file_name&orderDirection=asc&shareKey={shareKey}&SharePwd={pwd}&ParentFileId=0&Page=1&event" +
"=homeListFile&operateType=1";
public static Future<String> parse(String data, String code) {
private static final String DOWNLOAD_API_URL = "https://www.123pan.com/b/api/share/download/info?auth-key={authKey}";
public Future<String> parse(String data, String code) {
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
Promise<String> promise = Promise.promise();
@@ -37,7 +45,7 @@ public class YeTool {
Matcher matcher = compile.matcher(html);
if (!matcher.find()) {
System.out.println("err");
promise.fail(html + "\n Ye: " + dataKey + " 正则匹配失败");
return;
}
String fileInfoString = matcher.group(1);
@@ -59,13 +67,14 @@ public class YeTool {
.send().onSuccess(res2 -> {
JsonObject infoJson = res2.bodyAsJsonObject();
if (infoJson.getInteger("code") != 0) {
promise.fail("Ye: " + dataKey + " 状态码异常" + infoJson);
return;
}
JsonObject getFileInfoJson =
infoJson.getJsonObject("data").getJsonArray("InfoList").getJsonObject(0);
getFileInfoJson.put("ShareKey", shareKey);
getDownUrl(promise, client, getFileInfoJson);
});
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Ye", dataKey, t)));
} else {
promise.fail(dataKey + " 该分享需要密码");
}
@@ -75,17 +84,22 @@ public class YeTool {
JsonObject reqBodyJson = resListJson.getJsonObject("data").getJsonArray("InfoList").getJsonObject(0);
reqBodyJson.put("ShareKey", shareKey);
getDownUrl(promise, client, reqBodyJson);
});
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Ye", dataKey, t)));
return promise.future();
}
private static void getDownUrl(Promise<String> promise, WebClient client, JsonObject reqBodyJson) {
System.out.println(reqBodyJson);
client.postAbs("https://www.123pan.com/a/api/share/download/info").sendJsonObject(reqBodyJson).onSuccess(res2 -> {
log.info(reqBodyJson.encodePrettily());
client.postAbs(UriTemplate.of(DOWNLOAD_API_URL))
.setTemplateParam("authKey", AESUtils.getAuthKey())
.putHeader("Platform", "web")
.putHeader("App-Version", "3")
.sendJsonObject(reqBodyJson).onSuccess(res2 -> {
JsonObject downURLJson = res2.bodyAsJsonObject();
System.out.println(downURLJson);
if (downURLJson.getInteger("code") != 0) {
promise.fail("Ye: downURLJson返回值异常->" + downURLJson);
return;
}
String downURL = downURLJson.getJsonObject("data").getString("DownloadURL");
@@ -93,10 +107,23 @@ public class YeTool {
Map<String, String> urlParams = CommonUtils.getURLParams(downURL);
String params = urlParams.get("params");
byte[] decodeByte = Base64.getDecoder().decode(params);
promise.complete(new String(decodeByte));
String downUrl2 = new String(decodeByte);
// 获取直链
client.getAbs(downUrl2).send().onSuccess(res3 -> {
JsonObject res3Json = res3.bodyAsJsonObject();
if (res3Json.getInteger("code") != 0) {
promise.fail("Ye: downUrl2返回值异常->" + res3Json);
return;
}
promise.complete(res3Json.getJsonObject("data").getString("redirect_url"));
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Ye",
reqBodyJson.encodePrettily(), t)));
} catch (MalformedURLException e) {
promise.fail("urlParams解析异常" + e.getMessage());
}
});
}).onFailure(t -> promise.fail(PanExceptionUtils.fillRunTimeException("Ye", reqBodyJson.encodePrettily(), t)));
}
}

View File

@@ -4,12 +4,16 @@ import org.apache.commons.lang3.StringUtils;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import java.util.HexFormat;
import java.util.Random;
/**
* AES加解密工具类
@@ -236,4 +240,51 @@ public class AESUtils {
}
return result;
}
// ========================== musetransfer加密相关 ===========================
//length用户要求产生字符串的长度
public static String getRandomString(int length){
String str="abcdefghijklmnopqrstuvwxyz0123456789";
Random random=new Random();
StringBuilder sb=new StringBuilder();
for(int i=0;i<length;i++){
int number=random.nextInt(36);
sb.append(str.charAt(number));
}
return sb.toString();
}
public static String getRandomString(){
return getRandomString(10);
}
//=============================== 123pan加密相关 ===============================
public static String getMD5Str(String str) {
byte[] digest;
try {
MessageDigest md5 = MessageDigest.getInstance("md5");
digest = md5.digest(str.getBytes(StandardCharsets.UTF_8));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
//16是表示转换为16进制数
return new BigInteger(1, digest).toString(16);
}
public static String getAuthKey() {
String _0x2207af = "/b/api/share/download/info";
String _0x467baa = "web";
int _0x4965f1 = 3;
String _0x430930 = String.valueOf(Math.round(0x989680 * Math.random()));
String _0x53928f = String.valueOf(new Date().getTime() / 0x3e8);
String _0x49ec94 = getMD5Str(_0x53928f + "|" + _0x430930 + "|" + _0x2207af + "|" + _0x467baa + "|" + _0x4965f1
+ "|8-8D$sL8gPjom7bk#cY");
return _0x53928f + "-" + _0x430930 + "-" + _0x49ec94;
}
}

View File

@@ -1,80 +0,0 @@
package cn.qaiu.lz.common.util;
import cn.qaiu.vx.core.util.CastUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import java.util.Map;
/**
* 奶牛快传解析工具
*
* @author <a href="https://qaiu.top">QAIU</a>
* @date 2023/4/21 21:19
*/
@Slf4j
public class CowTool {
/*
First request:
{
"code": "0000",
"message": "success",
"data": {
"guid": "e4f41b51-b5da-4f60-9312-37aa10c0aad7",
"firstFile": {
"id": "23861191276513345",
}
}
}
Then request:
{
"code": "0000",
"message": "success",
"tn": "TN:DE0E092E8A464521983780FBA21D0CD3",
"data": {
"downloadUrl": "https://download.cowcs.com..."
}
}
*/
public static String parse(String fullUrl) throws Exception {
var uniqueUrl = fullUrl.substring(fullUrl.lastIndexOf('/') + 1);
var baseUrl = "https://cowtransfer.com/core/api/transfer/share";
var result = Jsoup
.connect(baseUrl + "?uniqueUrl=" + uniqueUrl).ignoreContentType(true)
.get()
.text();
var objectMapper = new ObjectMapper();
Map<String, Object> map = objectMapper.readValue(result, new TypeReference<>() {
});
if ("success".equals(map.get("message")) && map.containsKey("data")) {
Map<String, Object> data = CastUtil.cast(map.get("data"));
var guid = data.get("guid").toString();
Map<String, Object> firstFile = CastUtil.cast(data.get("firstFile"));
var fileId = firstFile.get("id").toString();
var result2 = Jsoup
.connect(baseUrl + "/download?transferGuid=" + guid + "&fileId=" + fileId)
.ignoreContentType(true)
.get()
.text();
Map<String, Object> map2 = objectMapper.readValue(result2, new TypeReference<>() {});
if ("success".equals(map2.get("message")) && map2.containsKey("data")) {
Map<String, Object> data2 = CastUtil.cast(map2.get("data"));
var downloadUrl = data2.get("downloadUrl").toString();
if (StringUtils.isNotEmpty(downloadUrl)) {
log.info("cow parse success: {}", downloadUrl);
return downloadUrl;
}
}
}
log.info("Cow parse field------------->end");
throw new Exception("Cow解析失败");
}
}

View File

@@ -1,65 +0,0 @@
package cn.qaiu.lz.common.util;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import io.vertx.uritemplate.UriTemplate;
import lombok.extern.slf4j.Slf4j;
/**
* 移动云空间解析
*/
@Slf4j
public class EcTool {
private static final String SHARE_URL_PREFIX = "https://www.ecpan.cn/drive/fileextoverrid" +
".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 static final String EC_HOST = "www.ecpan.cn";
public static Future<String> parse(String dataKey) {
Promise<String> promise = Promise.promise();
WebClient client = WebClient.create(VertxHolder.getVertxInstance());
// 第一次请求 获取文件信息
client.getAbs(UriTemplate.of(SHARE_URL_PREFIX)).setTemplateParam("dataKey", dataKey).send().onSuccess(res -> {
JsonObject jsonObject = res.bodyAsJsonObject();
log.debug("ecPan get file info -> {}", jsonObject);
JsonObject fileInfo = jsonObject
.getJsonObject("var")
.getJsonObject("chainFileInfo");
if (!fileInfo.containsKey("errMesg")) {
JsonObject cloudpFile = fileInfo.getJsonObject("cloudpFile");
JsonArray fileIdList = JsonArray.of(cloudpFile);
// 构造请求JSON {"extCodeFlag":0,"isIp":0}
JsonObject requestBodyJson = JsonObject.of("extCodeFlag", 0, "isIp", 0);
requestBodyJson.put("shareId", Integer.parseInt(fileInfo.getString("shareId"))); // 注意shareId
// 数据类型
requestBodyJson.put("groupId", cloudpFile.getString("groupId"));
requestBodyJson.put("fileIdList", fileInfo.getJsonArray("cloudpFileList"));
// 第二次请求 获取下载链接
client.postAbs(DOWNLOAD_REQUEST_URL)
.sendJsonObject(requestBodyJson).onSuccess(res2 -> {
JsonObject jsonRes = res2.bodyAsJsonObject();
log.debug("ecPan get download url -> {}", res2.body().toString());
promise.complete(jsonRes.getJsonObject("var").getString("downloadUrl"));
}).onFailure(t -> {
promise.fail(new RuntimeException("解析异常: key = " + dataKey, t.fillInStackTrace()));
});
} else {
promise.fail(new RuntimeException(DOWNLOAD_REQUEST_URL + " 解析失败: "
+ fileInfo.getString("errMesg")) + " key = " + dataKey);
}
}
).onFailure(t -> {
promise.fail(new RuntimeException("解析异常: key = " + dataKey, t.fillInStackTrace()));
});
return promise.future();
}
}

View File

@@ -1,7 +0,0 @@
package cn.qaiu.lz.common.util;
public interface IFuturePanTool {
}

View File

@@ -0,0 +1,12 @@
package cn.qaiu.lz.common.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());
}
}

View File

@@ -1,12 +1,12 @@
package cn.qaiu.lz.web.http;
import cn.qaiu.lz.common.util.*;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.parser.impl.*;
import cn.qaiu.lz.web.model.SysUser;
import cn.qaiu.lz.web.service.UserService;
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.model.JsonResult;
import cn.qaiu.vx.core.util.AsyncServiceUtil;
import io.vertx.core.Future;
import io.vertx.core.Promise;
@@ -34,207 +34,50 @@ public class ServerApi {
return userService.login(user);
}
@RouteMapping(value = "/test2", method = RouteMethod.GET)
public JsonResult<String> test01() {
return JsonResult.data("ok");
}
@RouteMapping(value = "/parser", method = RouteMethod.GET)
public Future<Void> parse(HttpServerResponse response, HttpServerRequest request, String url, String pwd) {
Promise<Void> promise = Promise.promise();
if (url.contains(EcTool.EC_HOST)) {
// 默认读取Url参数会被截断手动获取一下其他参数
String data = request.getParam("data");
EcTool.parse(data).onSuccess(resUrl -> {
response.putHeader("location", resUrl).setStatusCode(302).end();
promise.complete();
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
} else if (url.contains(UcTool.SHARE_URL_PREFIX)) {
UcTool.parse(url, pwd).onSuccess(resUrl -> {
response.putHeader("location", resUrl).setStatusCode(302).end();
promise.complete();
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
} else if (url.contains(FjTool.SHARE_URL_PREFIX)) {
FjTool.parse(url).onSuccess(resUrl -> {
response.putHeader("location", resUrl).setStatusCode(302).end();
promise.complete();
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
} else if (url.contains(FcTool.SHARE_URL_PREFIX)) {
FcTool.parse(url, pwd).onSuccess(resUrl -> {
response.putHeader("location", resUrl).setStatusCode(302).end();
promise.complete();
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
} else if (url.contains(YeTool.SHARE_URL_PREFIX)) {
YeTool.parse(url, pwd).onSuccess(resUrl -> {
response.putHeader("location", resUrl).setStatusCode(302).end();
promise.complete();
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
} else if (url.contains("lanzou")) {
try {
LzTool.parse(url, pwd).onSuccess(resUrl -> {
response.putHeader("location", resUrl).setStatusCode(302).end();
promise.complete();
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
} catch (Exception e) {
promise.fail(e);
}
} else if (url.contains("cowtransfer.com")) {
String urlDownload;
try {
urlDownload = CowTool.parse(url);
response.putHeader("location", urlDownload).setStatusCode(302).end();
promise.complete();
} catch (Exception e) {
promise.fail(e);
}
Promise<Void> promise = Promise.promise();
if (url.contains(EcTool.SHARE_URL_PREFIX)) {
// 默认读取Url参数会被截断手动获取一下其他参数
url = EcTool.SHARE_URL_PREFIX + request.getParam("data");
}
try {
IPanTool.shareURLPrefixMatching(url).parse(url, pwd).onSuccess(resUrl -> {
response.putHeader("location", resUrl).setStatusCode(302).end();
promise.complete();
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
} catch (Exception e) {
promise.fail(e);
}
return promise.future();
}
@RouteMapping(value = "/lz/:id", method = RouteMethod.GET)
public void lzParse(HttpServerResponse response, String id) {
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET)
public void parseKey(HttpServerResponse response, String type, String key) {
String code = "";
if (id.contains("@")) {
String[] ids = id.split("@");
id = ids[0];
code = ids[1];
if (key.contains("@")) {
String[] keys = key.split("@");
key = keys[0];
code = keys[1];
}
LzTool.parse(id, code).onSuccess(resUrl -> response.putHeader("location", resUrl)
IPanTool.typeMatching(type).parse(key, code).onSuccess(resUrl -> response.putHeader("location", resUrl)
.setStatusCode(302).end()).onFailure(t -> {
response.putHeader(CONTENT_TYPE, "text/html;charset=utf-8");
response.end(t.getMessage());
});
}
@RouteMapping(value = "/json/lz/:id", method = RouteMethod.GET)
public Future<String> lzParseJson(HttpServerResponse response, String id) {
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET)
public Future<String> parseKeyJson(HttpServerResponse response, String type, String key) {
String code = "";
if (id.contains("@")) {
String[] ids = id.split("@");
id = ids[0];
code = ids[1];
if (key.contains("@")) {
String[] keys = key.split("@");
key = keys[0];
code = keys[1];
}
return LzTool.parse(id, code);
}
@RouteMapping(value = "/cow/:id", method = RouteMethod.GET)
public void cowParse(HttpServerResponse response, String id) throws Exception {
var url = "https://cowtransfer.com/s/" + id;
var urlDownload = CowTool.parse(url);
response.putHeader("location", urlDownload).setStatusCode(302).end();
}
@RouteMapping(value = "/json/cow/:id", method = RouteMethod.GET)
public JsonResult<String> cowParseJson(HttpServerResponse response, String id) throws Exception {
var url = "https://cowtransfer.com/s/" + id;
return JsonResult.data(CowTool.parse(url));
}
@RouteMapping(value = "/ec/:id", method = RouteMethod.GET)
public void ecParse(HttpServerResponse response, String id) {
EcTool.parse(id).onSuccess(resUrl -> response.putHeader("location", resUrl)
.setStatusCode(302).end()).onFailure(t -> {
response.putHeader(CONTENT_TYPE, "text/html;charset=utf-8");
response.end(t.getMessage());
});
}
@RouteMapping(value = "/json/ec/:id", method = RouteMethod.GET)
public Future<String> ecParseJson(HttpServerResponse response, String id) {
return EcTool.parse(id);
}
@RouteMapping(value = "/uc/:id", method = RouteMethod.GET)
public void ucParse(HttpServerResponse response, String id) {
String code = "";
if (id.contains("@")) {
String[] ids = id.split("@");
id = ids[0];
code = ids[1];
}
UcTool.parse(id, code).onSuccess(resUrl -> response.putHeader("location", resUrl)
.setStatusCode(302).end()).onFailure(t -> {
response.putHeader(CONTENT_TYPE, "text/html;charset=utf-8");
response.end(t.getMessage());
});
}
@RouteMapping(value = "/json/uc/:id", method = RouteMethod.GET)
public Future<String> ucParseJson(String id) {
String code = "";
if (id.contains("@")) {
String[] ids = id.split("@");
id = ids[0];
code = ids[1];
}
return UcTool.parse(id, code);
}
@RouteMapping(value = "/fj/:id", method = RouteMethod.GET)
public void fjParse(HttpServerResponse response, String id) {
FjTool.parse(id).onSuccess(resUrl -> response.putHeader("location", resUrl)
.setStatusCode(302).end()).onFailure(t -> {
response.putHeader(CONTENT_TYPE, "text/html;charset=utf-8");
response.end(t.getMessage());
});
}
@RouteMapping(value = "/json/fj/:id", method = RouteMethod.GET)
public Future<String> fjParseJson(HttpServerResponse response, String id) {
return FjTool.parse(id);
}
@RouteMapping(value = "/fc/:id", method = RouteMethod.GET)
public void fcParse(HttpServerResponse response, String id) {
String code = "";
if (id.contains("@")) {
String[] ids = id.split("@");
id = ids[0];
code = ids[1];
}
FcTool.parse(id, code).onSuccess(resUrl -> response.putHeader("location", resUrl)
.setStatusCode(302).end()).onFailure(t -> {
response.putHeader(CONTENT_TYPE, "text/html;charset=utf-8");
response.end(t.getMessage());
});
}
@RouteMapping(value = "/json/fc/:id", method = RouteMethod.GET)
public Future<String> fcParseJson(HttpServerResponse response, String id) {
String code = "";
if (id.contains("@")) {
String[] ids = id.split("@");
id = ids[0];
code = ids[1];
}
return FcTool.parse(id, code);
}
@RouteMapping(value = "/ye/:id", method = RouteMethod.GET)
public void YeParse(HttpServerResponse response, String id) {
String code = "";
if (id.contains("@")) {
String[] ids = id.split("@");
id = ids[0];
code = ids[1];
}
YeTool.parse(id, code).onSuccess(resUrl -> response.putHeader("location", resUrl)
.setStatusCode(302).end()).onFailure(t -> {
response.putHeader(CONTENT_TYPE, "text/html;charset=utf-8");
response.end(t.getMessage());
});
}
@RouteMapping(value = "/json/ye/:id", method = RouteMethod.GET)
public Future<String> YeParseJson(HttpServerResponse response, String id) {
String code = "";
if (id.contains("@")) {
String[] ids = id.split("@");
id = ids[0];
code = ids[1];
}
return YeTool.parse(id, code);
return IPanTool.typeMatching(type).parse(key, code);
}
}

View File

@@ -10,25 +10,29 @@ server:
# 反向代理服务器配置路径(不用加后缀)
proxyConf: server-proxy
# vertx线程配置 事件循环线程配置为0: eventLoopPoolSize将会采用默认配置(CPU核心*2) workerPoolSize将会采用默认20
vertx:
eventLoopPoolSize: 8
workerPoolSize: 20
eventLoopPoolSize: 0
workerPoolSize: 0
custom:
asyncServiceInstances: 8
# 异步服务线程数
asyncServiceInstances: 4
# server路由(controller层)所在包路径
routerLocations: cn.qaiu.lz.web.http
# 拦截器包路径
interceptorClassPath: cn.qaiu.lz.common.interceptorImpl.DefaultInterceptor
# server层包路径
handlerLocations: cn.qaiu.lz.web.service
# 匹配规则
ignoresReg:
- .*/login$
- .*/test.*$
# 实体类包路径匹配正则
entityPackagesReg:
- ^cn\.qaiu\.lz\.web\.model\..*
errorPage404: /index.html
indexPage: /test2
sharedLogin: true
lzConfig:
config: '111'
# 数据源配置
dataSource:
provider_class: io.vertx.ext.jdbc.spi.impl.HikariCPDataSourceProvider
jdbcUrl: jdbc:h2:tcp://127.0.0.1:9095/./db/myData;MODE=MySQL;DATABASE_TO_LOWER=FALSE

View File

@@ -2,6 +2,6 @@
active: dev
# 框架版本号 和主版本号
version_vertx: 4.4.1
version_app: 0.1.3
version_app: 0.1.6
# 公司名称 -> LOGO版权文字
copyright: QAIU

View File

@@ -0,0 +1,60 @@
###
//https://cowtransfer.com/s/e4f41b51b5da4f
https://cowtransfer.com/core/api/transfer/share?uniqueUrl=9a644fe3e3a748
###
https://cowtransfer.com/core/api/transfer/share?uniqueUrl=e4f41b51b5da4f
###
https://cowtransfer.com/core/api/transfer/share/download?transferGuid=e4f41b51-b5da-4f60-9312-37aa10c0aad7&fileId=23861191276513345
//https://download.cowcs.com/cowtransfer/cowtransfer/29188/db32e132e69f490eb4a343b398990f4b.docx?auth_key=1682111861-7b9579fbebb84aaba6bca368d083ab12-0-cbf009f3ffbcbb86191b8cdbc103abce&biz_type=1&business_code=COW_TRANSFER&channel_code=COW_CN_WEB&response-content-disposition=attachment%3B%20filename%3D05-CGB-DB-MENU-V1.02.docx%3Bfilename*%3Dutf-8%27%2705-CGB-DB-MENU-V1.02.docx&user_id=1023860921943729188&x-verify=1
#V2 api
###
https://cowtransfer.com/core/api/dam/asset/files/download/23890683765739569/url/v2
authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDc1OTMwNGItMDEwZS00MGJiLTlhNDUtZTczY2Q5ODYzMDQwIiwiZXhwIjoxNjgzNzkzOTExfQ.rE9z0vhogPjUC0I92MqU8U_PKe4j_mGn7lGgPFMGt5c
###
POST https://cowtransfer.com/api/user/emaillogin
Content-Type: multipart/form-data; boundary=WebAppBoundary
--WebAppBoundary
Content-Disposition: form-data; name="email"
111@sad.com
--WebAppBoundary
Content-Disposition: form-data; name="password"
11111
--WebAppBoundary--
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDc1OTMwNGItMDEwZS00MGJiLTlhNDUtZTczY2Q5ODYzMDQwIiwiZXhwIjoxNjg0NDYyMjMzfQ.AeuB5-aQUlgudoLDRgvFodlHx2qKiPFx3BAqGA0R-eE
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDc1OTMwNGItMDEwZS00MGJiLTlhNDUtZTczY2Q5ODYzMDQwIiwiZXhwIjoxNjg0NDYyMjMzfQ.AeuB5-aQUlgudoLDRgvFodlHx2qKiPFx3BAqGA0R-eE
###
POST https://cowtransfer.com/api/user/register/emailregistrationcheck
Content-Type: multipart/form-data; boundary=WebAppBoundary
--WebAppBoundary
Content-Disposition: form-data; name="email"
736226400@qq.com
--WebAppBoundary
Content-Disposition: form-data; name="joinProToken"
--WebAppBoundary--
###
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch web-service/src/main/resources/http-tools/test.http' --prune-empty --tag-name-filter cat -- --all
git push origin master --force
rmdir .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

View File

@@ -10,3 +10,18 @@ content-type: application/x-www-form-urlencoded;
task=5&folder_id=-1
###
POST https://wwsd.lanzoue.com/ajaxm.php
Accept: application/json, text/javascript, */*
Content-Type: application/x-www-form-urlencoded
action=downprocess&sign=AGYHOQk4BjdTWgQ7BzcGOlU_bATVSNQMxBDFQZgZoBj4HMFEgWnMOZ1I1A2NWOgUxB20HMlM_aUGoLOgQz&p=e4k4
###
https://developer.lanzoug.com/file/?VDJbZVxtADFSWwY+U2YHa1FuU2tTYgBnBnUGZFNmWylSOVMpCTJQZQQhBSdQKQFkBjMOfAMyA21VPVs8V28BLVRkWyJcMAB6UjcGfVNlB29RZVN4U3YAawZxBiZTaVszUj5TYQkLUG0ENgVuUDUBMQZkDjsDbgMxVWBbZFc8ASZUMlt/XDwAZVIyBmFTNwc3UTtTYFM8ACQGcQZwUzJbaFJiUzYJZ1ArBGIFZlArATIGaQ4kAz8DMlVnWz9XMwE1VGZbOFw3AG1SPQY0UzcHMVFqUzdTbwBmBjEGMVNsW2NSM1MzCWxQNwRkBWVQNwEwBmQOPwNyA3tVO1stVy0BdVQnW2lccwA9UmAGbVM2BzZRPlNvUzgAOwY5BiZTe1szUj9TYQkyUDkEYwVjUDQBMAZsDiUDcgMnVTRbMVd8AT1UZVs6XDkAYVI0BmBTNQc/UTlTZVMqAHcGcQZ3UzJba1JkUzwJYVA0BGIFYVA0ATAGaw4tAykDaFUiW2BXOgExVGZbIlwzAGdSNgZ%2BUzYHMlE9U3hTOwA6
###
https://developer.lanzoug.com/file/?VTNVa1tqAjMFDAM7BDEAbAE+U2tfbgZhBnVbOQUwVCYEb1IoAToCNwQhVnRXLlcyVWAEdl9uVzkEbFYxVm5VeVVlVSxbNwJ4BWADeAQyAGgBNVN4X3oGbQZxW3sFP1Q8BGhSYAEDAj8ENlY9VzJXZ1U3BDFfMldlBDFWaVY9VXJVM1VxWzsCZwVlA2QEYAAwAWtTYF8wBiIGcVstBWRUZwQ0UjcBbwJ5BGJWNVcsV2RVOgQuX2NXZgQ2VjJWMlVhVWdVNlswAm8FagMxBGAANgE6UzdfYwZgBjFbbAU6VGwEZVIyAWQCZQRkVjZXMFdmVTcENV8uVy8EalYgVixVIVUmVWdbdAI/BTcDaARhADEBblNvXzQGPQY5W3sFLVQ8BGlSYAE6AmsEY1Y2VztXY1U/BDFfM1dnBDRWZFYkVXpVc1VkW2oCIQVuA2QEZgA5AW5TZl8wBjYGMFtkBWFUcwRxUnUBKwJrBGNWNlc7V2NVPwQxXzJXZQQzVmdWLFUhVTxVcls7AmcFYgNnBH4AMwFoU2RfLgY1BjVbawV3VGIEPA==

View File

@@ -0,0 +1,56 @@
###
POST https://service.tezign.com/transfer/share/download
Content-Type: application/json;charset=UTF-8
X-Transfer-Device:a14a17ccc07
X-Transfer-Sign:bea1e0bece50692b5d062d6d809cdb75
{"code":"a85hmqm1g"}
###
POST https://service.tezign.com/transfer/share/download
Content-Type: application/json;charset=UTF-8
X-Transfer-Device:xz9ynai8bi
X-Transfer-Sign:923690974149080012f9eba9ddc0cd82
{"code":"81nq4gm8j", "pwd":"3184"}
###
https://musetransfer.com/s/a85hmqm1g
###
#https://musetransfer.com/s/81nq4gm8j 密码3184请点击链接获取《无主题 - sdgsEasyManual.doc》, 有效期至2024年6月13日
#https://musetransfer.com/s/a85hmqm1g 请点击链接获取《111》, 有效期至2024年6月13日
# a85hmqm1g
# X-Transfer-Device:a14a17cc207
# X-Transfer-Sign:fc29338ac476fb1bee7d2eb12bb30bca
#
# MD5(L3RyYW5zZmVyL3NoYXJlL2Rvd25sb2Fk|eyJjb2RlIjoiYTg1aG1xbTFnIn0=|a14a17cc207|)=fc29338ac476fb1bee7d2eb12bb30bca
#
# L3RyYW5zZmVyL3NoYXJlL2Rvd25sb2Fk|eyJjb2RlIjoiODFucTRnbThqIn0=|8g54fpq1we|
#
# L3RyYW5zZmVyL3NoYXJlL2Rvd25sb2Fk /transfer/share/download
# eyJjb2RlIjoiYTg1aG1xbTFnIn0= {"code":"a85hmqm1g"}
#
# a14a17cc207 随机数
#
# ƒ u(t){var e="",n=t||11,u=(1+n)/2|0;(!o||i+u>c)&&(o=r(c),i=0);while(u--)e+=a[o[i++]];return e.substring(0,n)}
# 'L3RyYW5zZmVyL3NoYXJlL2Rvd25sb2Fk|eyJjb2RlIjoiODFucTRnbThqIiwgICJwd2QiOiIzMTg0In0=|xz9ynai8bi|'
###
https://transfer.musecdn1.com/a85hmqm1g/d43a1b3baf646501368a7cc2a087ae8e.zip?auth_key=1686790800-YTg1aG1xbTFnOjE1OTU1NA-0-19b281e3e5fefedac6c57272deb6e747&response-content-type=application/octet-stream&response-content-disposition=attachment%3B%20filename%3D%22netdisk-fast-download-0.1.6-bin.zip%22%3B%20filename%2A%3DUTF-8%27%27netdisk-fast-download-0.1.6-bin.zip
Referer:https://musetransfer.com/
###
# @no-cookie-jar
# @no-redirect
https://transfer.musecdn1.com/81nq4gm8j/2333dd8e48fbf52f787d633d506c2d8d.doc?auth_key=1686798000-ODFucTRnbThqOjE1OTU1NA-0-7f9f1174bf1cc9925ed5ad497c6344b8&response-content-type=application/octet-stream&response-content-disposition=attachment%3B%20filename%3D%22sdgsEasyManual.doc%22%3B%20filename%2A%3DUTF-8%27%27sdgsEasyManual.doc
Referer:https://musetransfer.com/

View File

@@ -82,3 +82,26 @@ Content-Type:application/json;charset=UTF-8
},
"publicPath": "https://www.123pan.com/a/api/"
}
###
https://www.123pan.com/b/api/share/get?limit=100&next=1&orderBy=file_name&orderDirection=asc&shareKey=iaKtVv-6OECd&SharePwd=DcGe&ParentFileId=0&Page=1&event=homeListFile&operateType=1&auth-key=1689990170-3703305-62c1030a465013b7c18eddb49156b67b
# 23/07/22 123pan添加header
###
POST https://www.123pan.com/b/api/share/download/info?auth-key=1689996626-2926572-49f4cde9a911469869ee264cb6ae8426
App-Version:3
Platform:web
{"ShareKey":"iaKtVv-6OECd","FileID":2193732,"S3keyFlag":"1811834632-0","Size":4203111,"Etag":"69c94adbc0b9190cf23c4e958d8c7c53"}
###
https://www.123pan.com/a/api/share/get?limit=100&next=1&orderBy=file_name&orderDirection=asc&shareKey=iaKtVv-6OECd&SharePwd=DcGe&ParentFileId=0&Page=1&event=homeListFile&operateType=1

View File

@@ -1,146 +1,65 @@
###
# @no-redirect
GET http://127.0.0.1:6400/api/serverApi/test3?fullUrl=https://wwp.lanzoux.com/iNvid035jgcb
###
### 蓝奏云
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://lanzoux.com/ia2cntg
###
### 蓝奏云
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://lanzoux.com/iNvid035jgcb
###
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://cowtransfer.com/s/9a644fe3e3a748
###
# @no-redirect
GET http://127.0.0.1:6400/cow/9a644fe3e3a748
###
### 蓝奏云
# @no-redirect
GET http://127.0.0.1:6400/lz/i6SqHmp1yfc
###
### 蓝奏云
# @no-redirect
GET http://127.0.0.1:6400/lz/icBp6qqj82b@QAIU
###
### 蓝奏云
GET http://127.0.0.1:6400/json/lz/ia2cntg
###
### 奶牛
GET http://127.0.0.1:6400/json/cow/9a644fe3e3a748
###
//https://cowtransfer.com/s/9a644fe3e3a748
https://cowtransfer.com/core/api/transfer/share?uniqueUrl=9a644fe3e3a748
###
https://cowtransfer.com/core/api/transfer/share?uniqueUrl=e4f41b51b5da4f
###
https://cowtransfer.com/core/api/transfer/share/download?transferGuid=e4f41b51-b5da-4f60-9312-37aa10c0aad7&fileId=23861191276513345
//https://download.cowcs.com/cowtransfer/cowtransfer/29188/db32e132e69f490eb4a343b398990f4b.docx?auth_key=1682111861-7b9579fbebb84aaba6bca368d083ab12-0-cbf009f3ffbcbb86191b8cdbc103abce&biz_type=1&business_code=COW_TRANSFER&channel_code=COW_CN_WEB&response-content-disposition=attachment%3B%20filename%3D05-CGB-DB-MENU-V1.02.docx%3Bfilename*%3Dutf-8%27%2705-CGB-DB-MENU-V1.02.docx&user_id=1023860921943729188&x-verify=1
#V2 api
###
https://cowtransfer.com/core/api/dam/asset/files/download/23890683765739569/url/v2
authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDc1OTMwNGItMDEwZS00MGJiLTlhNDUtZTczY2Q5ODYzMDQwIiwiZXhwIjoxNjgzNzkzOTExfQ.rE9z0vhogPjUC0I92MqU8U_PKe4j_mGn7lGgPFMGt5c
###
POST https://cowtransfer.com/api/user/emaillogin
Content-Type: multipart/form-data; boundary=WebAppBoundary
--WebAppBoundary
Content-Disposition: form-data; name="email"
111@sad.com
--WebAppBoundary
Content-Disposition: form-data; name="password"
11111
--WebAppBoundary--
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDc1OTMwNGItMDEwZS00MGJiLTlhNDUtZTczY2Q5ODYzMDQwIiwiZXhwIjoxNjg0NDYyMjMzfQ.AeuB5-aQUlgudoLDRgvFodlHx2qKiPFx3BAqGA0R-eE
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDc1OTMwNGItMDEwZS00MGJiLTlhNDUtZTczY2Q5ODYzMDQwIiwiZXhwIjoxNjg0NDYyMjMzfQ.AeuB5-aQUlgudoLDRgvFodlHx2qKiPFx3BAqGA0R-eE
###
POST https://cowtransfer.com/api/user/register/emailregistrationcheck
Content-Type: multipart/form-data; boundary=WebAppBoundary
--WebAppBoundary
Content-Disposition: form-data; name="email"
736226400@qq.com
--WebAppBoundary
Content-Disposition: form-data; name="joinProToken"
--WebAppBoundary--
### Send a form with the text and file fields
POST https://httpbin.org/post
Content-Type: multipart/form-data; boundary=WebAppBoundary
--WebAppBoundary
Content-Disposition: form-data; name="element-name"
Content-Type: text/plain
Name
--WebAppBoundary
Content-Disposition: form-data; name="data"; filename="data.json"
Content-Type: application/json
< ./request-form-data.json
--WebAppBoundary--
### 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
# https://www.ecpan.cn/drive/fileextoverrid.do?chainUrlTemplate=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=aa0cae0164d8885e6d35826b5b2901eckbWJBalM&parentId=-1
GET http://127.0.0.1:6400/cow/e4f41b51b5da4f
###
### 奶牛
# @no-redirect
GET http://127.0.0.1:6400/ec/aa0cae0164d8885e6d35826b5b2901eckbWJBalM1
GET http://127.0.0.1:6400/parser?url=https://cowtransfer.com/s/9a644fe3e3a748
### 移动云空间 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
### 移动云空间 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
###
GET http://127.0.0.1:6400/json/ec/aa0cae0164d8885e6d35826b5b2901eckbWJBalM
###
### 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
###
POST https://wwsd.lanzoue.com/ajaxm.php
Accept: application/json, text/javascript, */*
Content-Type: application/x-www-form-urlencoded
action=downprocess&sign=AGYHOQk4BjdTWgQ7BzcGOlU_bATVSNQMxBDFQZgZoBj4HMFEgWnMOZ1I1A2NWOgUxB20HMlM_aUGoLOgQz&p=e4k4
###
https://developer.lanzoug.com/file/?VDJbZVxtADFSWwY+U2YHa1FuU2tTYgBnBnUGZFNmWylSOVMpCTJQZQQhBSdQKQFkBjMOfAMyA21VPVs8V28BLVRkWyJcMAB6UjcGfVNlB29RZVN4U3YAawZxBiZTaVszUj5TYQkLUG0ENgVuUDUBMQZkDjsDbgMxVWBbZFc8ASZUMlt/XDwAZVIyBmFTNwc3UTtTYFM8ACQGcQZwUzJbaFJiUzYJZ1ArBGIFZlArATIGaQ4kAz8DMlVnWz9XMwE1VGZbOFw3AG1SPQY0UzcHMVFqUzdTbwBmBjEGMVNsW2NSM1MzCWxQNwRkBWVQNwEwBmQOPwNyA3tVO1stVy0BdVQnW2lccwA9UmAGbVM2BzZRPlNvUzgAOwY5BiZTe1szUj9TYQkyUDkEYwVjUDQBMAZsDiUDcgMnVTRbMVd8AT1UZVs6XDkAYVI0BmBTNQc/UTlTZVMqAHcGcQZ3UzJba1JkUzwJYVA0BGIFYVA0ATAGaw4tAykDaFUiW2BXOgExVGZbIlwzAGdSNgZ%2BUzYHMlE9U3hTOwA6
###
https://developer.lanzoug.com/file/?VTNVa1tqAjMFDAM7BDEAbAE+U2tfbgZhBnVbOQUwVCYEb1IoAToCNwQhVnRXLlcyVWAEdl9uVzkEbFYxVm5VeVVlVSxbNwJ4BWADeAQyAGgBNVN4X3oGbQZxW3sFP1Q8BGhSYAEDAj8ENlY9VzJXZ1U3BDFfMldlBDFWaVY9VXJVM1VxWzsCZwVlA2QEYAAwAWtTYF8wBiIGcVstBWRUZwQ0UjcBbwJ5BGJWNVcsV2RVOgQuX2NXZgQ2VjJWMlVhVWdVNlswAm8FagMxBGAANgE6UzdfYwZgBjFbbAU6VGwEZVIyAWQCZQRkVjZXMFdmVTcENV8uVy8EalYgVixVIVUmVWdbdAI/BTcDaARhADEBblNvXzQGPQY5W3sFLVQ8BGlSYAE6AmsEY1Y2VztXY1U/BDFfM1dnBDRWZFYkVXpVc1VkW2oCIQVuA2QEZgA5AW5TZl8wBjYGMFtkBWFUcwRxUnUBKwJrBGNWNlc7V2NVPwQxXzJXZQQzVmdWLFUhVTxVcls7AmcFYgNnBH4AMwFoU2RfLgY1BjVbawV3VGIEPA==
### 小飞机盘
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://www.feijix.com/s/tIfhRqH
###
### 小飞机盘
GET http://127.0.0.1:6400/json/fj/tIfhRqH
###
### 小飞机盘
# @no-redirect
GET http://127.0.0.1:6400/fj/tIfhRqH
@@ -148,22 +67,27 @@ GET http://127.0.0.1:6400/fj/tIfhRqH
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://v2.fangcloud.com/sharing/e5079007dc31226096628870c7&pwd=QAIU
###
### 360亿方云
GET http://127.0.0.1:6400/json/fc/30646fefc8bf936a4766ab8a5e
###
### 360亿方云
# @no-redirect
GET http://127.0.0.1:6400/fc/e5079007dc31226096628870c7@QAIU
#https://v2.fangcloud.com/sharing/e5079007dc31226096628870c7
# https://www.123pan.com/s/iaKtVv-ICECd.html
### 123
GET http://127.0.0.1:6400/json/ye/iaKtVv-qOECd
GET http://127.0.0.1:6400/json/ye/iaKtVv-ICECd
### 123
# @no-redirect
GET http://127.0.0.1:6400/ye/iaKtVv-qOECd@asdads
GET http://127.0.0.1:6400/ye/iaKtVv-qOECd
### 123
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.html&pwd=DcGe
###
POST http://127.0.0.1:6400/login1

View File

@@ -28,3 +28,14 @@ server-name: Vert.x-proxy-server(v4.1.2)
# sock:
# - path: /real/
# origin: 127.0.0.1:8088
#proxy:
# - listen: 8085
# static:
# path: /mu-down
# # add-headers:
# # x-token: ABC
# root: webroot/mu-down/
# index: index.html

View File

@@ -54,4 +54,27 @@ public class TestAESUtil {
public void testTs() {
System.out.println(System.currentTimeMillis());
}
@Test
public void testRandom() {
System.out.println(AESUtils.getRandomString());
System.out.println(AESUtils.getRandomString());
System.out.println(AESUtils.getRandomString());
System.out.println(AESUtils.getRandomString());
}
@Test
public void testKeyAuth(){
System.out.println(AESUtils.getAuthKey());
System.out.println(AESUtils.getAuthKey());
System.out.println(AESUtils.getAuthKey());
}
@Test
public void testAES2() throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
BadPaddingException, InvalidKeyException {
System.out.println(AESUtils.encryptBase64ByAES("AAAAA", "123123"));
System.out.println(AESUtils.encryptBase64ByAES("AAAAA", AESUtils.generateKey("123123")));
}
}

File diff suppressed because one or more lines are too long

7
webroot/mu-down/a.html Normal file
View File

@@ -0,0 +1,7 @@
<html lang="en">
<body>
<iframe
src="data:text/html;base64,PGZvcm0gbWV0aG9kPXBvc3QgYWN0aW9uPWh0dHA6Ly9hLmIuY29tL2Q+PGlucHV0IHR5cGU9dGV4dCBuYW1lPSdpZCcgdmFsdWU9JzEyMycvPjwvZm9ybT48c2NyaXB0PmRvY3VtZW50LmZvcm1zWzBdLnN1Ym1pdCgpOzwvc2NyaXB0Pg==">
</iframe>
</body>
</html>

View File

@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>musetransfer文件下载中间页面</title></head>
<body>
<script src="jquery.min.js"></script>
<h1 id="msg"></h1>
<script>
const data = location.search.substring(1);
const url = window.atob(data);
console.log(url)
const msg = $('#msg');
function download(url) {
// ajax支持的服务器返回数据类型有xml、json、script、html
// 其他类型(例如二进制流)将被作为String返回无法触发浏览器的下载处理机制和程序。
// 从Response Headers中获取fileName
let fileName = /filename="(.*)"/g.exec(decodeURIComponent(url))[1]
if (!fileName) {
msg.text('解析文件名异常')
return
}
msg.text('解析成功, Ajax下载完成后会自动保存, 如果文件较大可能需要一定时间请耐心等待')
$.ajax({
url: url,
method: "get",
xhrFields: {responseType: "blob"},
beforeSend: function (request) {
// request.setRequestHeader("token", sessionStorage.getItem('token'));
request.setRequestHeader("token", sessionStorage.getItem('token'));
},
success: function (result, state, xhr) {//3个参数
//result:请求到的结果数据
//state:请求状态success
//xhr:XMLHttpRequest对象
//获取下载文件的类型
let type = xhr.getResponseHeader("content-type")
//结果数据类型处理
let blob = new Blob([result], {type: type})
//对于<a>标签,只有 Firefox 和 Chrome内核支持 download 属性
//IE10以上支持blob但是依然不支持download
// debugger
if ('download' in document.createElement('a')) {//支持a标签download的浏览器
//通过创建a标签实现
let link = document.createElement("a");
//文件名
link.download = fileName;
link.style.display = "none"
link.href = URL.createObjectURL(blob);
document.body.appendChild(link);
link.click();//执行下载
URL.revokeObjectURL(link.href);//释放url
document.body.removeChild(link);//释放标签
} else {//不支持
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName)
}
}
},
})
}
if (url.length > 16) {
download(url)
} else {
msg.text('解析URL异常')
}
</script>
</body>

2
webroot/mu-down/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long