mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-01-13 01:44:12 +00:00
22
web-service/src/main/java/cn/qaiu/lz/common/ToJson.java
Normal file
22
web-service/src/main/java/cn/qaiu/lz/common/ToJson.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package cn.qaiu.lz.common;
|
||||
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
/**
|
||||
* lz-web <br>
|
||||
* 实现此接口 POJO转JSON对象
|
||||
*
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* <br>Create date 2021/8/27 11:40
|
||||
*/
|
||||
public interface ToJson {
|
||||
|
||||
/**
|
||||
* POJO转JSON对象
|
||||
*
|
||||
* @return Json Object
|
||||
*/
|
||||
default JsonObject toJson() {
|
||||
return JsonObject.mapFrom(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.qaiu.lz.common.interceptorImpl;
|
||||
|
||||
import cn.qaiu.vx.core.base.BaseHttpApi;
|
||||
import cn.qaiu.vx.core.interceptor.Interceptor;
|
||||
import cn.qaiu.vx.core.model.JsonResult;
|
||||
import cn.qaiu.vx.core.util.CommonUtil;
|
||||
import cn.qaiu.vx.core.util.SharedDataUtil;
|
||||
import cn.qaiu.vx.core.util.VertxHolder;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.shareddata.LocalMap;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.val;
|
||||
|
||||
/**
|
||||
* 默认拦截器实现
|
||||
* 校验用户是否合法 <br>
|
||||
* TODO 暂时只做简单实现
|
||||
*/
|
||||
@Slf4j
|
||||
public class DefaultInterceptor implements Interceptor, BaseHttpApi {
|
||||
|
||||
private final JsonArray ignores = SharedDataUtil.getJsonArrayForCustomConfig("ignoresReg");
|
||||
|
||||
@Override
|
||||
public void handle(RoutingContext ctx) {
|
||||
ctx.next();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.qaiu.lz.common.model;
|
||||
|
||||
import io.vertx.codegen.annotations.DataObject;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* <br>Create date 2021/7/22 3:34
|
||||
*/
|
||||
@DataObject
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class MyData implements Serializable {
|
||||
public static final long serialVersionUID = 1L;
|
||||
|
||||
private String id;
|
||||
|
||||
private String maxSize;
|
||||
|
||||
|
||||
public MyData(JsonObject jsonObject) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.qaiu.lz.common.model;
|
||||
|
||||
import cn.qaiu.lz.common.ToJson;
|
||||
import io.vertx.codegen.annotations.DataObject;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* lz-web
|
||||
*
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* <br>Create date 2021/8/10 11:10
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@DataObject
|
||||
public class UserInfo implements ToJson {
|
||||
|
||||
private String username;
|
||||
|
||||
private String permission;
|
||||
|
||||
private String pwdCrc32;
|
||||
|
||||
private String uuid;
|
||||
|
||||
public UserInfo(JsonObject jsonObject) {
|
||||
this.username = jsonObject.getString("username");
|
||||
this.permission = jsonObject.getString("permission");
|
||||
this.pwdCrc32 = jsonObject.getString("pwdCrc32");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.qaiu.lz.common.util;
|
||||
|
||||
public class ArrayUtil {
|
||||
|
||||
public static int[] parseIntArray(String[] arr) {
|
||||
int[] ints = new int[arr.length];
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
ints[i] = Integer.parseInt(arr[i]);
|
||||
}
|
||||
return ints;
|
||||
}
|
||||
|
||||
public static float[] parseFloatArray(String[] arr) {
|
||||
float[] ints = new float[arr.length];
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
ints[i] = Float.parseFloat(arr[i]);
|
||||
}
|
||||
return ints;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cn.qaiu.lz.common.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 获取连接
|
||||
*
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
*/
|
||||
public enum ConnectUtil {
|
||||
|
||||
// 实现枚举单例
|
||||
INSTANCE;
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConnectUtil.class);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
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解析失败");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
65
web-service/src/main/java/cn/qaiu/lz/common/util/EcTool.java
Normal file
65
web-service/src/main/java/cn/qaiu/lz/common/util/EcTool.java
Normal file
@@ -0,0 +1,65 @@
|
||||
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 FULL_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(FULL_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();
|
||||
}
|
||||
}
|
||||
95
web-service/src/main/java/cn/qaiu/lz/common/util/LzTool.java
Normal file
95
web-service/src/main/java/cn/qaiu/lz/common/util/LzTool.java
Normal file
@@ -0,0 +1,95 @@
|
||||
package cn.qaiu.lz.common.util;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.jsoup.Jsoup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 蓝奏云解析工具
|
||||
*
|
||||
* @author QAIU
|
||||
* @version 1.0 update 2021/5/16 10:39
|
||||
*/
|
||||
public class LzTool {
|
||||
|
||||
public static String parse(String fullUrl) throws Exception {
|
||||
var userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.3";
|
||||
var url = fullUrl.substring(0, fullUrl.lastIndexOf('/') + 1);
|
||||
var id = fullUrl.substring(fullUrl.lastIndexOf('/') + 1);
|
||||
Map<String, String> header = new HashMap<>();
|
||||
header.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
|
||||
header.put("referer", url);
|
||||
|
||||
/*
|
||||
// 部分链接需要设置安卓UA
|
||||
sec-ch-ua: "Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"
|
||||
sec-ch-ua-mobile: ?1
|
||||
sec-ch-ua-platform: "Android"
|
||||
*/
|
||||
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36";
|
||||
Map<String, String> header2 = new HashMap<>();
|
||||
header2.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
|
||||
header2.put("sec-ch-ua-mobile", "sec-ch-ua-mobile");
|
||||
header2.put("sec-ch-ua-platform", "Android");
|
||||
header2.put("referer", url);
|
||||
|
||||
//第一次请求,获取iframe的地址
|
||||
String result = Jsoup.connect(url + id)
|
||||
.userAgent(userAgent)
|
||||
.get()
|
||||
.select(".ifr2")
|
||||
.attr("src");
|
||||
|
||||
//第二次请求得到js里的json数据里的sign
|
||||
result = Jsoup.connect(url + result)
|
||||
.headers(header)
|
||||
.userAgent(userAgent)
|
||||
.get()
|
||||
.html();
|
||||
// System.out.println(result);
|
||||
// 'sign':'AWcGOFprUGFWX1BvBTVXawdrBDZTOAU_bV2FTZFU7W2sBJ1t4DW0FYFIyBmgDZVJgUjAFNV41UGQFNg_c_c' 改下正则TMD 最近上传竟然没_c_c
|
||||
Matcher matcher = Pattern.compile("'sign'\s*:\s*'([0-9a-zA-Z_]+)'").matcher(result);
|
||||
Map<String, String> params = new LinkedHashMap<>();
|
||||
if (matcher.find()) {
|
||||
String sn = matcher.group(1).replace("'", "");
|
||||
params.put("action", "downprocess");
|
||||
params.put("sign", sn);
|
||||
params.put("ves", "1");
|
||||
// System.out.println(sn);
|
||||
|
||||
} else {
|
||||
throw new IOException();
|
||||
}
|
||||
//第三次请求 通过参数发起post请求,返回json数据
|
||||
result = Jsoup
|
||||
.connect(url + "ajaxm.php")
|
||||
.headers(header)
|
||||
.userAgent(userAgent)
|
||||
.data(params)
|
||||
.post()
|
||||
.text()
|
||||
.replace("\\", "");
|
||||
//json转为map
|
||||
params = new ObjectMapper().readValue(result, new TypeReference<>() {});
|
||||
// System.out.println(params);
|
||||
//通过json的数据拼接出最终的URL发起第最终请求,并得到响应信息头
|
||||
url = params.get("dom") + "/file/" + params.get("url");
|
||||
var headers = Jsoup.connect(url)
|
||||
.ignoreContentType(true)
|
||||
.userAgent(userAgent2)
|
||||
.headers(header2)
|
||||
.followRedirects(false)
|
||||
.execute()
|
||||
.headers();
|
||||
//得到重定向的地址进行重定向
|
||||
url = headers.get("Location");
|
||||
return url;
|
||||
}
|
||||
}
|
||||
87
web-service/src/main/java/cn/qaiu/lz/common/util/UcTool.java
Normal file
87
web-service/src/main/java/cn/qaiu/lz/common/util/UcTool.java
Normal file
@@ -0,0 +1,87 @@
|
||||
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 UcTool {
|
||||
private static final String API_URL_PREFIX = "https://pc-api.uc.cn/1/clouddrive/";
|
||||
|
||||
public static final String FULL_URL_PREFIX = "https://fast.uc.cn/s/";
|
||||
|
||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "share/sharepage/token?entry=ft&fr=pc&pr" +
|
||||
"=UCBrowser";
|
||||
|
||||
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "transfer_share/detail?pwd_id={pwd_id}&passcode" +
|
||||
"={passcode}&stoken={stoken}";
|
||||
|
||||
private static final String THIRD_REQUEST_URL = API_URL_PREFIX + "file/download?entry=ft&fr=pc&pr=UCBrowser";
|
||||
|
||||
public static Future<String> parse(String data, String code) {
|
||||
if (!data.startsWith(FULL_URL_PREFIX)) {
|
||||
data = FULL_URL_PREFIX + data;
|
||||
}
|
||||
var passcode = (code == null) ? "" : code;
|
||||
var dataKey = data.substring(FULL_URL_PREFIX.length());
|
||||
Promise<String> promise = Promise.promise();
|
||||
var client = WebClient.create(VertxHolder.getVertxInstance());
|
||||
var jsonObject = JsonObject.of("share_for_transfer", true);
|
||||
jsonObject.put("pwd_id", dataKey);
|
||||
jsonObject.put("passcode", passcode);
|
||||
// 第一次请求 获取文件信息
|
||||
client.postAbs(FIRST_REQUEST_URL).sendJsonObject(jsonObject).onSuccess(res -> {
|
||||
log.debug("第一阶段 {}", res.body());
|
||||
var resJson = res.bodyAsJsonObject();
|
||||
if (resJson.getInteger("code") != 0) {
|
||||
promise.fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
return;
|
||||
}
|
||||
var stoken = resJson.getJsonObject("data").getString("stoken");
|
||||
// 第二次请求
|
||||
client.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.setTemplateParam("pwd_id", dataKey)
|
||||
.setTemplateParam("passcode", passcode)
|
||||
.setTemplateParam("stoken", stoken)
|
||||
.send().onSuccess(res2 -> {
|
||||
log.debug("第二阶段 {}", res2.body());
|
||||
JsonObject resJson2 = res2.bodyAsJsonObject();
|
||||
if (resJson2.getInteger("code") != 0) {
|
||||
promise.fail(FIRST_REQUEST_URL + " 返回异常: " + resJson2);
|
||||
return;
|
||||
}
|
||||
// 文件信息
|
||||
var info = resJson2.getJsonObject("data").getJsonArray("list").getJsonObject(0);
|
||||
// 第二次请求
|
||||
var bodyJson = JsonObject.of()
|
||||
.put("fids", JsonArray.of(info.getString("fid")))
|
||||
.put("pwd_id", dataKey)
|
||||
.put("stoken", stoken)
|
||||
.put("fids_token", JsonArray.of(info.getString("share_fid_token")));
|
||||
client.postAbs(THIRD_REQUEST_URL).sendJsonObject(bodyJson)
|
||||
.onSuccess(res3 -> {
|
||||
log.debug("第三阶段 {}", res3.body());
|
||||
var resJson3 = res3.bodyAsJsonObject();
|
||||
if (resJson3.getInteger("code") != 0) {
|
||||
promise.fail(FIRST_REQUEST_URL + " 返回异常: " + resJson2);
|
||||
return;
|
||||
}
|
||||
promise.complete(resJson3.getJsonArray("data").getJsonObject(0).getString("download_url"));
|
||||
})
|
||||
.onFailure(t -> promise
|
||||
.fail(new RuntimeException("解析异常: ", t.fillInStackTrace())));
|
||||
|
||||
}).onFailure(t -> promise.fail(new RuntimeException("解析异常: ", t.fillInStackTrace())));
|
||||
}
|
||||
).onFailure(t -> promise.fail(new RuntimeException("解析异常: key = " + dataKey, t.fillInStackTrace())));
|
||||
return promise.future();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user