mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 04:13:03 +00:00
30
README.md
30
README.md
@@ -30,7 +30,7 @@
|
||||
- [奶牛快传 (cow)](https://cowtransfer.com/)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [移动云空间 (ec)](https://www.ecpan.cn/web)
|
||||
- [移动云云空间 (ec)](https://www.ecpan.cn/web)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [小飞机网盘 (fj)](https://www.feijipan.com/)
|
||||
@@ -67,7 +67,7 @@ your_host指的是您的域名或者IP,实际使用时替换为实际域名或
|
||||
- 直链JSON: `通用接口`和`标志短链`前加上`/json` 加密分享的密码规则同上;
|
||||
- 网盘标识参考上面网盘支持情况
|
||||
- 括号内是可选内容: 表示当带有分享密码时需要加上密码参数
|
||||
- 移动云空间,小飞机网盘的加密分享的密码可以忽略
|
||||
- 移动云云空间,小飞机网盘的加密分享的密码可以忽略
|
||||
- 移动云空间分享key取分享链接中的data参数,比如`&data=xxx`的参数就是xxx
|
||||
|
||||
规则示例:
|
||||
@@ -80,7 +80,7 @@ your_host指的是您的域名或者IP,实际使用时替换为实际域名或
|
||||
http://your_host/json/parser?url=分享链接(&pwd=xxx)
|
||||
http://your_host/json/网盘标识/分享key(@分享密码)
|
||||
3. 需要特殊处理的网盘分享:
|
||||
1. 移动云空间(ec)使用parser?url= 解析时因为分享链接比较特殊(链接带有参数且含有#符号)所以要么对#进行转义%23要么直接去掉# 或者URL直接是主机名+'/'跟一个data参数
|
||||
1. 移动云云空间(ec)使用parser?url= 解析时因为分享链接比较特殊(链接带有参数且含有#符号)所以要么对#进行转义%23要么直接去掉# 或者URL直接是主机名+'/'跟一个data参数
|
||||
比如 http://your_host/parser?url=https://www.ecpan.cn/web//yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
http://your_host/parser?url=https://www.ecpan.cn/web/%23/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
http://your_host/parser?url=https://www.ecpan.cn/&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
@@ -146,17 +146,17 @@ GET http://127.0.0.1:6400/json/fc/e5079007dc31226096628870c7@QAIU
|
||||
# 网盘对比
|
||||
|
||||
|
||||
| 网盘名称 | 可直接下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 | 登录接口 |
|
||||
|------------|------------------------|----------|-----------|-----------------|------|
|
||||
| 蓝奏云 | √ | √ | 不限空间 | 100M | TODO |
|
||||
| 奶牛快传 | √ | X | 10G | 不限大小 | TODO |
|
||||
| 移动云空间 | √ | √(密码可忽略) | 5G(个人) | 不限大小 | TODO |
|
||||
| UC网盘 | 需要登录 | √ | 10G | 不限大小 | TODO |
|
||||
| 小飞机网盘 | √ | √(密码可忽略) | 10G | 不限大小 | TODO |
|
||||
| 360亿方云 | √(试用账号有时间限制企业版需要599续费) | √(密码可忽略) | 100G(须实名) | 不限大小 | TODO |
|
||||
| 123云盘 | √ | √ | 2T | 100G(>100M需要登录) | TODO |
|
||||
| 文叔叔(TODO) | √(注意有时间限制) | √ | 10G | 5GB | TODO |
|
||||
| 夸克网盘(TODO) | 需要登录 | √ | 10G | 不限大小 | TODO |
|
||||
| 网盘名称 | 免登陆下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 |
|
||||
|-------------|---------|----------|-----------|-----------------|
|
||||
| 蓝奏云 | √ | √ | 不限空间 | 100M |
|
||||
| 奶牛快传 | √ | X | 10G | 不限大小 |
|
||||
| 移动云云空间(个人版) | √ | √(密码可忽略) | 5G(个人) | 不限大小 |
|
||||
| 小飞机网盘 | √ | √(密码可忽略) | 10G | 不限大小 |
|
||||
| 360亿方云 | √ | √(密码可忽略) | 100G(须实名) | 不限大小 |
|
||||
| 123云盘 | √ | √ | 2T | 100G(>100M需要登录) |
|
||||
| 文叔叔 | √ | √ | 10G | 5GB |
|
||||
| 夸克网盘 | x | √ | 10G | 不限大小 |
|
||||
| UC网盘 | x | √ | 10G | 不限大小 |
|
||||
|
||||
# 打包部署
|
||||
|
||||
@@ -164,7 +164,7 @@ GET http://127.0.0.1:6400/json/fc/e5079007dc31226096628870c7@QAIU
|
||||
- [阿里jdk17(Dragonwell17-windows-x86)](https://lz.qaiu.top/ec/e957acef36ce89e1053979672a90d219n)
|
||||
- [阿里jdk17(Dragonwell17-linux-x86)](https://lz.qaiu.top/ec/6ebc9f2e0bbd53b4c4d5b11013f40a80NHvcYU)
|
||||
- [阿里jdk17(Dragonwell17-linux-aarch64)](https://lz.qaiu.top/ec/d14c2d06296f61b52a876b525265e0f8tzxTc5)
|
||||
- [解析有效性测试-移动云空间-阿里jdk17-linux-x86](https://lz.qaiu.top/json/ec/6ebc9f2e0bbd53b4c4d5b11013f40a80NHvcYU)
|
||||
- [解析有效性测试-移动云云空间-阿里jdk17-linux-x86](https://lz.qaiu.top/json/ec/6ebc9f2e0bbd53b4c4d5b11013f40a80NHvcYU)
|
||||
|
||||
## 开发和打包
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import io.vertx.uritemplate.UriTemplate;
|
||||
*/
|
||||
public class EcTool extends PanBase implements IPanTool {
|
||||
private static final String FIRST_REQUEST_URL = "https://www.ecpan.cn/drive/fileextoverrid" +
|
||||
".do?chainUrlTemplate=https:%2F%2Fwww.ecpan" +
|
||||
".do?extractionCode={extractionCode}&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";
|
||||
@@ -27,7 +27,11 @@ public class EcTool extends PanBase implements IPanTool {
|
||||
public Future<String> parse() {
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
// 第一次请求 获取文件信息
|
||||
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("dataKey", dataKey).send().onSuccess(res -> {
|
||||
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL))
|
||||
.setTemplateParam("dataKey", dataKey)
|
||||
.setTemplateParam("extractionCode", pwd == null ? "" : pwd)
|
||||
.send()
|
||||
.onSuccess(res -> {
|
||||
JsonObject jsonObject = res.bodyAsJsonObject();
|
||||
log.debug("ecPan get file info -> {}", jsonObject);
|
||||
JsonObject fileInfo = jsonObject
|
||||
@@ -37,6 +41,11 @@ public class EcTool extends PanBase implements IPanTool {
|
||||
fail("{} 解析失败:{} key = {}", FIRST_REQUEST_URL, fileInfo.getString("errMesg"), dataKey);
|
||||
return;
|
||||
}
|
||||
if (!fileInfo.containsKey("cloudpFile")) {
|
||||
fail("{} 解析失败:cloudpFile不存在 key = {}", FIRST_REQUEST_URL, dataKey);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject cloudpFile = fileInfo.getJsonObject("cloudpFile");
|
||||
JsonArray fileIdList = JsonArray.of(cloudpFile);
|
||||
// 构造请求JSON {"extCodeFlag":0,"isIp":0}
|
||||
|
||||
@@ -4,14 +4,14 @@ import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.AESUtils;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import cn.qaiu.util.UUIDUtil;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.buffer.Buffer;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import io.vertx.ext.web.client.HttpRequest;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 小飞机网盘
|
||||
*
|
||||
@@ -24,11 +24,19 @@ public class FjTool extends PanBase implements IPanTool {
|
||||
public static final String SHARE_URL_PREFIX2 = REFERER_URL + "s/";
|
||||
private static final String API_URL_PREFIX = "https://api.feijipan.com/ws/";
|
||||
|
||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome&extra" +
|
||||
"=2&shareId={shareId}&type=0&offset=1&limit=60";
|
||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" +
|
||||
"&uuid={uuid}&extra=2×tamp={ts}&shareId={shareId}&type=0&offset=1&limit=60";
|
||||
/// recommend/list?devType=6&devModel=Chrome&uuid={uuid}&extra=2×tamp={ts}&shareId={shareId}&type=0&offset=1
|
||||
// &limit=60
|
||||
|
||||
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "file/redirect?downloadId={fidEncode}&enable=1" +
|
||||
"&devType=6&uuid={uuid}×tamp={ts}&auth={auth}";
|
||||
"&devType=6&uuid={uuid}×tamp={ts}&auth={auth}&shareId={dataKey}";
|
||||
// https://api.feijipan.com/ws/file/redirect?downloadId={fidEncode}&enable=1&devType=6&uuid={uuid}×tamp={ts}&auth={auth}&shareId={dataKey}
|
||||
|
||||
|
||||
private static final String VIP_REQUEST_URL = API_URL_PREFIX + "/buy/vip/list?devType=6&devModel=Chrome&uuid" +
|
||||
"={uuid}&extra=2×tamp={ts}";
|
||||
// https://api.feijipan.com/ws/buy/vip/list?devType=6&devModel=Chrome&uuid=WQAl5yBy1naGudJEILBvE&extra=2×tamp=E2C53155F6D09417A27981561134CB73
|
||||
|
||||
public FjTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
@@ -42,49 +50,68 @@ public class FjTool extends PanBase implements IPanTool {
|
||||
dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
}
|
||||
|
||||
WebClient client = clientNoRedirects;
|
||||
String shareId = String.valueOf(AESUtils.idEncrypt(dataKey));
|
||||
long nowTs = System.currentTimeMillis();
|
||||
String tsEncode = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
||||
String uuid = UUIDUtil.fjUuid(); // 也可以使用 UUID.randomUUID().toString()
|
||||
|
||||
// 第一次请求 获取文件信息
|
||||
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
||||
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("shareId", shareId).send().onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
if (resJson.getInteger("code") != 200) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
return;
|
||||
}
|
||||
if (resJson.getJsonArray("list").size() == 0) {
|
||||
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
|
||||
return;
|
||||
}
|
||||
// 文件Id
|
||||
JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0);
|
||||
String fileId = fileInfo.getString("fileIds");
|
||||
String userId = fileInfo.getString("userId");
|
||||
// 其他参数
|
||||
long nowTs = System.currentTimeMillis();
|
||||
String tsEncode = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String fidEncode = AESUtils.encrypt2Hex(fileId + "|");
|
||||
String auth = AESUtils.encrypt2Hex(fileId + "|" + nowTs);
|
||||
// 24.5.12 飞机盘 规则修改 需要固定UUID先请求会员接口, 再请求后续接口
|
||||
client.postAbs(UriTemplate.of(VIP_REQUEST_URL))
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
.send().onSuccess(r0 -> { // 忽略res
|
||||
|
||||
MultiMap headers0 = MultiMap.caseInsensitiveMultiMap();
|
||||
headers0.set("referer", REFERER_URL);
|
||||
// 第二次请求
|
||||
client.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.putHeaders(headers0)
|
||||
.setTemplateParam("fidEncode", fidEncode)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
.setTemplateParam("auth", auth).send().onSuccess(res2 -> {
|
||||
MultiMap headers = res2.headers();
|
||||
if (!headers.contains("Location")) {
|
||||
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers());
|
||||
return;
|
||||
}
|
||||
promise.complete(headers.get("Location"));
|
||||
}).onFailure(handleFail(SECOND_REQUEST_URL));
|
||||
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
long nowTs0 = System.currentTimeMillis();
|
||||
String tsEncode0 = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
||||
// 第一次请求 获取文件信息
|
||||
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
||||
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL))
|
||||
.setTemplateParam("shareId", shareId)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode0)
|
||||
.send().onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
if (resJson.getInteger("code") != 200) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
return;
|
||||
}
|
||||
if (resJson.getJsonArray("list").size() == 0) {
|
||||
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
|
||||
return;
|
||||
}
|
||||
// 文件Id
|
||||
JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0);
|
||||
String fileId = fileInfo.getString("fileIds");
|
||||
String userId = fileInfo.getString("userId");
|
||||
// 其他参数
|
||||
long nowTs2 = System.currentTimeMillis();
|
||||
String tsEncode2 = AESUtils.encrypt2Hex(Long.toString(nowTs2));
|
||||
String fidEncode = AESUtils.encrypt2Hex(fileId + "|" + userId);
|
||||
String auth = AESUtils.encrypt2Hex(fileId + "|" + nowTs2);
|
||||
|
||||
MultiMap headers0 = MultiMap.caseInsensitiveMultiMap();
|
||||
headers0.set("referer", REFERER_URL);
|
||||
// 第二次请求
|
||||
HttpRequest<Buffer> httpRequest =
|
||||
clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.putHeaders(headers0)
|
||||
.setTemplateParam("fidEncode", fidEncode)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode2)
|
||||
.setTemplateParam("auth", auth)
|
||||
.setTemplateParam("dataKey", dataKey);
|
||||
System.out.println(httpRequest.toString());
|
||||
httpRequest.send().onSuccess(res2 -> {
|
||||
MultiMap headers = res2.headers();
|
||||
if (!headers.contains("Location")) {
|
||||
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers());
|
||||
return;
|
||||
}
|
||||
promise.complete(headers.get("Location"));
|
||||
}).onFailure(handleFail(SECOND_REQUEST_URL));
|
||||
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
|
||||
}).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -34,12 +33,12 @@ public class IzTool extends PanBase implements IPanTool {
|
||||
public Future<String> parse() {
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
|
||||
WebClient client = clientNoRedirects;
|
||||
String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey));
|
||||
// 24.5.12 ilanzou改规则无需计算shareId
|
||||
// String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey));
|
||||
|
||||
// 第一次请求 获取文件信息
|
||||
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
||||
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("shareId", shareId).send().onSuccess(res -> {
|
||||
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("shareId", dataKey).send().onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
if (resJson.getInteger("code") != 200) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
@@ -61,7 +60,7 @@ public class IzTool extends PanBase implements IPanTool {
|
||||
String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId);
|
||||
String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs);
|
||||
// 第二次请求
|
||||
client.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.setTemplateParam("fidEncode", fidEncode)
|
||||
.setTemplateParam("uuid", uuid)
|
||||
.setTemplateParam("ts", tsEncode)
|
||||
|
||||
@@ -246,6 +246,7 @@ public class AESUtils {
|
||||
return idEncrypt0(str, array, 2, 2);
|
||||
}
|
||||
|
||||
|
||||
// ================================蓝奏优享版Id解密========================================== //
|
||||
public static int idEncryptIz(String str) {
|
||||
// idEncrypt(e) {
|
||||
|
||||
35
parser/src/main/java/cn/qaiu/util/UUIDUtil.java
Normal file
35
parser/src/main/java/cn/qaiu/util/UUIDUtil.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package cn.qaiu.util;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/5/13 4:10
|
||||
*/
|
||||
public class UUIDUtil {
|
||||
|
||||
public static String fjUuid() {
|
||||
return generateRandomString(21);
|
||||
}
|
||||
|
||||
public static String generateRandomString(int length) {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] randomBytes = new byte[length];
|
||||
random.nextBytes(randomBytes);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : randomBytes) {
|
||||
int value = b & 0x3F; // 63 in hexadecimal
|
||||
if (value < 36) {
|
||||
sb.append(Integer.toString(value, 36));
|
||||
} else if (value < 62) {
|
||||
sb.append(Character.toUpperCase(Integer.toString(value - 26, 36).charAt(0)));
|
||||
} else if (value > 62) {
|
||||
sb.append("-");
|
||||
} else { // value == 62 or 63
|
||||
sb.append("_");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user