Compare commits

...

8 Commits

Author SHA1 Message Date
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
28 changed files with 375 additions and 484 deletions

View File

@@ -1,52 +1,64 @@
# 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)
## 网盘支持情况:
` 网盘名称(网盘标识): `
`网盘名称(网盘标识):`
- 蓝奏云 (lz)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- 奶牛快传 (cow)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- 移动云空间 (ec)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- UC网盘 (uc)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- 小飞机网盘 (fj)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- 亿方云 (fc)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- 123云盘 (ye)
- [ ] 登录, 上传, 下载, 分享
- [x] 直链解析
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- 文叔叔 (ws)
- 夸克网盘 (qk)
- TODO
技术栈:
Jdk17+Vert.x4.4.1+Jsoup
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/网盘标识/分享id(@分享密码)
2. 获取解析后的直链--JSON格式
http(s)://your_hostname/json/网盘标识/分享id(@分享密码)
3. 有些网盘的加密分享的密码可以忽略: 如移动云空间,小飞机网盘
http(s)://your_host/json/网盘标识/分享id(@分享密码)
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,12 +93,9 @@ GET http://127.0.0.1:6400/json/fc/e5079007dc31226096628870c7@QAIU
```
TODO:
解析蓝奏云加密链接
# 网盘对比
| 网盘名称 | 可直接下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 | 登录接口 |
|------------|------------|----------|-----------|---------|------|
| 蓝奏云 | √ | √ | 不限空间 | 100M | TODO |
@@ -100,4 +109,31 @@ 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.5-bin.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.5-releases/netdisk-fast-download-0.1.5-bin.zip
unzip netdisk-fast-download-*-bin.zip
cd netdisk-fast-download-*-bin
bash service-install.sh
```
## Windows服务部署
1. 解压netdisk-fast-download-0.1.5-bin.zip
2. 进入netdisk-fast-download-0.1.5-bin目录
3. 使用管理员权限运行nfd-service-install.bat
如果不想使用服务运行可以直接运行run.bat
## 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
# 注意修改为自己的路径
ExecStart=/usr/bin/java -server -Xmx128m -jar /root/java/netdisk-fast-download/netdisk-fast-download*.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>-jar ${jar} -server -Xmx128m </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

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

View File

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

View File

@@ -78,7 +78,8 @@ public class RouterHandlerFactory implements BaseHttpApi {
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();
});
@@ -199,8 +200,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 {
@@ -349,7 +350,7 @@ public class RouterHandlerFactory implements BaseHttpApi {
((Future<?>) data).onSuccess(res -> {
if (res instanceof JsonObject) {
fireJsonResponse(ctx, res);
} else if (res != null){
} else if (res != null) {
fireJsonResponse(ctx, JsonResult.data(res));
}
}).onFailure(e -> fireJsonResponse(ctx, JsonResult.error(e.getMessage())));
@@ -363,7 +364,7 @@ public class RouterHandlerFactory implements BaseHttpApi {
String err = e.getMessage();
if (e.getCause() != null) {
if (e.getCause() instanceof InvocationTargetException) {
err = ((InvocationTargetException)e.getCause()).getTargetException().getMessage();
err = ((InvocationTargetException) e.getCause()).getTargetException().getMessage();
} else {
err = e.getCause().getMessage();
}

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.5</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.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<version>0.1.3</version>
<version>0.1.5</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.5</version>
</dependency>

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,56 @@
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.util.CommonUtils;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
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(Vertx.vertx());
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);
}
} else {
log.error("cow parse fail: {}; json: {}", url2, res2Json);
}
});
} else {
log.error("cow parse fail: {}; json: {}", key, resJson);
}
});
return promise.future();
}
}

View File

@@ -1,5 +1,7 @@
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.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.Promise;
@@ -13,20 +15,21 @@ import lombok.extern.slf4j.Slf4j;
* 移动云空间解析
*/
@Slf4j
public class EcTool {
private static final String SHARE_URL_PREFIX = "https://www.ecpan.cn/drive/fileextoverrid" +
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 EC_HOST = "www.ecpan.cn";
public static final String SHARE_URL_PREFIX = "www.ecpan.cn/";
public static Future<String> parse(String dataKey) {
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(SHARE_URL_PREFIX)).setTemplateParam("dataKey", dataKey).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
@@ -48,9 +51,7 @@ public class EcTool {
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()));
});
}).onFailure(t -> promise.fail(new RuntimeException("解析异常: key = " + dataKey, t.fillInStackTrace())));
} else {
promise.fail(new RuntimeException(DOWNLOAD_REQUEST_URL + " 解析失败: "

View File

@@ -1,5 +1,7 @@
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.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
@@ -20,14 +22,14 @@ import java.util.regex.Pattern;
/**
* 亿方云
*/
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();

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.AESUtils;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
@@ -16,7 +19,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 +30,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();

View File

@@ -1,5 +1,6 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
@@ -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;

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,7 @@
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.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.Promise;
@@ -13,7 +15,7 @@ import lombok.extern.slf4j.Slf4j;
* 移动云空间解析
*/
@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 +28,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();

View File

@@ -1,5 +1,7 @@
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.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.Promise;
@@ -17,14 +19,14 @@ import java.util.regex.Pattern;
/**
* 123网盘
*/
public class YeTool {
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) {
public Future<String> parse(String data, String code) {
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
Promise<String> promise = Promise.promise();

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,7 +0,0 @@
package cn.qaiu.lz.common.util;
public interface IFuturePanTool {
}

View File

@@ -1,6 +1,7 @@
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;
@@ -34,207 +35,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

@@ -2,6 +2,6 @@
active: dev
# 框架版本号 和主版本号
version_vertx: 4.4.1
version_app: 0.1.3
version_app: 0.1.5
# 公司名称 -> 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,9 +10,10 @@ 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
GET http://127.0.0.1:6400/cow/e4f41b51b5da4f
###
# @no-redirect
@@ -29,82 +30,14 @@ 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
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/aa0cae0164d8885e6d35826b5b2901eckbWJBalM1
GET http://127.0.0.1:6400/ec/81027a5c99af5b11ca004966c945cce6W9Bf2
###
GET http://127.0.0.1:6400/json/ec/aa0cae0164d8885e6d35826b5b2901eckbWJBalM
@@ -167,3 +100,6 @@ GET http://127.0.0.1:6400/ye/iaKtVv-qOECd@asdads
### 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

File diff suppressed because one or more lines are too long