mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 20:33:03 +00:00
1. 添加缓存
2. 优化解析架构 3. 优化核心模块
This commit is contained in:
21
README.md
21
README.md
@@ -49,6 +49,9 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
|
||||
- [Cloudreve自建网盘 (ce)](https://github.com/cloudreve/Cloudreve)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析
|
||||
- [超星网盘-教育用户专属推荐非常稳定! (cx)](https://passport2.chaoxing.com/login?newversion=true&refer=https%3A%2F%2Fpan-yz.chaoxing.com%2F)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [ ] 直链解析
|
||||
- [QQ邮箱 (qq) 暂不可用-存在cookie问题](https://wx.mail.qq.com/)
|
||||
- [ ] 登录, 上传, 下载, 分享
|
||||
- [X] 直链解析(用户无法直接使用直链)
|
||||
@@ -71,7 +74,7 @@ your_host指的是您的域名或者IP,实际使用时替换为实际域名或
|
||||
- 移动云云空间,小飞机网盘的加密分享的密码可以忽略
|
||||
- 移动云空间分享key取分享链接中的data参数,比如`&data=xxx`的参数就是xxx
|
||||
|
||||
规则示例:
|
||||
API规则:
|
||||
```
|
||||
|
||||
1. 解析并自动302跳转 :
|
||||
@@ -80,20 +83,6 @@ your_host指的是您的域名或者IP,实际使用时替换为实际域名或
|
||||
2. 获取解析后的直链--JSON格式
|
||||
http://your_host/json/parser?url=分享链接(&pwd=xxx)
|
||||
http://your_host/json/网盘标识/分享key(@分享密码)
|
||||
3. 需要特殊处理的网盘分享:
|
||||
1. 移动云云空间(ec)使用parser?url= 解析时因为分享链接比较特殊(链接带有参数且含有#符号)所以要么对#进行转义%23要么直接去掉# 或者URL直接是主机名+'/'跟一个data参数
|
||||
比如 http://your_host/parser?url=https://www.ecpan.cn/web//yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
http://your_host/parser?url=https://www.ecpan.cn/web/%23/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
http://your_host/parser?url=https://www.ecpan.cn/&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
|
||||
2. Cloudreve自建网盘解析规则:
|
||||
1. 标志短链: 根据网盘使用https和http选择 http://your_host/ce/https_网盘域名_s_wDz5TK 或 http://your_host/ce/http_网盘域名_s_wDz5TK
|
||||
网盘域名指的是Cloudreve搭建网盘的主域名比如pan.huang1111.cn,如果存在子路径需要将/替换为_,是否存在子路径看分享链接格式是否是://网盘域名/子路径/s/xxx,一般不存在子路径:网盘域名/s/xxx,
|
||||
比如: http://127.0.0.1:6400/ce/https_pan.huang1111.cn_s_wDz5TK
|
||||
2. parser接口 -> http://your_host/parser?url=分享链接(&pwd=xxx)
|
||||
比如: http://127.0.0.1:6400/parser?url=https://pan.huang1111.cn/s/wDz5TK
|
||||
|
||||
|
||||
|
||||
```
|
||||
json返回数据格式示例:
|
||||
@@ -180,7 +169,7 @@ mvn package
|
||||
> 注意: netdisk-fast-download.service中的ExecStart的路径改为实际路径
|
||||
```shell
|
||||
cd ~
|
||||
wget -O netdisk-fast-download.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.7-release-fixed2/netdisk-fast-download-bin-fixed2.zip
|
||||
wget -O netdisk-fast-download.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.8-release-fixed2/netdisk-fast-download-bin-fixed2.zip
|
||||
unzip netdisk-fast-download-bin.zip
|
||||
cd netdisk-fast-download
|
||||
bash service-install.sh
|
||||
|
||||
@@ -32,12 +32,14 @@ import org.slf4j.LoggerFactory;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.qaiu.vx.core.util.ConfigConstant.ROUTE_TIME_OUT;
|
||||
import static io.vertx.core.http.HttpHeaders.*;
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||
|
||||
/**
|
||||
* 路由映射, 参数绑定
|
||||
@@ -87,6 +89,7 @@ public class RouterHandlerFactory implements BaseHttpApi {
|
||||
LOGGER.debug("The HTTP service request address information ===>path:{}, uri:{}, method:{}",
|
||||
ctx.request().path(), ctx.request().absoluteURI(), ctx.request().method());
|
||||
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
ctx.response().headers().add(DATE, LocalDateTime.now().format(ISO_LOCAL_DATE_TIME));
|
||||
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, OPTIONS, PUT, DELETE, HEAD");
|
||||
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_HEADERS, "X-PINGOTHER, Origin,Content-Type, Accept, " +
|
||||
"X-Requested-With, Dev, Authorization, Version, Token");
|
||||
|
||||
@@ -6,6 +6,7 @@ public interface ConfigConstant {
|
||||
String EVENT_LOOP_POOL_SIZE = "eventLoopPoolSize";
|
||||
String LOCAL = "local";
|
||||
String SERVER = "server";
|
||||
String CACHE = "cache";
|
||||
String GLOBAL_CONFIG = "globalConfig";
|
||||
String CUSTOM_CONFIG = "customConfig";
|
||||
String ASYNC_SERVICE_INSTANCES = "asyncServiceInstances";
|
||||
|
||||
114
parser/src/main/java/cn/qaiu/entity/ShareLinkInfo.java
Normal file
114
parser/src/main/java/cn/qaiu/entity/ShareLinkInfo.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package cn.qaiu.entity;
|
||||
|
||||
public class ShareLinkInfo {
|
||||
|
||||
private String shareKey; // 分享键
|
||||
private String type; // 分享类型
|
||||
private String sharePassword; // 分享密码(如果存在)
|
||||
private String shareUrl; // 原始分享链接
|
||||
private String standardUrl; // 规范化的标准链接
|
||||
|
||||
private ShareLinkInfo(Builder builder) {
|
||||
this.shareKey = builder.shareKey;
|
||||
this.type = builder.type;
|
||||
this.sharePassword = builder.sharePassword;
|
||||
this.shareUrl = builder.shareUrl;
|
||||
this.standardUrl = builder.standardUrl;
|
||||
}
|
||||
|
||||
// Getter和Setter方法
|
||||
|
||||
public String getShareKey() {
|
||||
return shareKey;
|
||||
}
|
||||
|
||||
public void setShareKey(String shareKey) {
|
||||
this.shareKey = shareKey;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getSharePassword() {
|
||||
return sharePassword;
|
||||
}
|
||||
|
||||
public void setSharePassword(String sharePassword) {
|
||||
this.sharePassword = sharePassword;
|
||||
}
|
||||
|
||||
public String getShareUrl() {
|
||||
return shareUrl;
|
||||
}
|
||||
|
||||
public void setShareUrl(String shareUrl) {
|
||||
this.shareUrl = shareUrl;
|
||||
}
|
||||
|
||||
public String getStandardUrl() {
|
||||
return standardUrl;
|
||||
}
|
||||
|
||||
public void setStandardUrl(String standardUrl) {
|
||||
this.standardUrl = standardUrl;
|
||||
}
|
||||
|
||||
// 静态方法创建建造者对象
|
||||
public static ShareLinkInfo.Builder newBuilder() {
|
||||
return new ShareLinkInfo.Builder();
|
||||
}
|
||||
|
||||
// 建造者类
|
||||
public static class Builder {
|
||||
private String shareKey; // 分享键
|
||||
private String type; // 分享类型
|
||||
private String sharePassword = ""; // 分享密码(如果存在)
|
||||
private String shareUrl; // 原始分享链接
|
||||
private String standardUrl; // 规范化的标准链接
|
||||
|
||||
public Builder shareKey(String shareKey) {
|
||||
this.shareKey = shareKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder type(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sharePassword(String sharePassword) {
|
||||
this.sharePassword = sharePassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder shareUrl(String shareUrl) {
|
||||
this.shareUrl = shareUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder standardUrl(String standardUrl) {
|
||||
this.standardUrl = standardUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShareLinkInfo build() {
|
||||
return new ShareLinkInfo(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ShareLinkInfo{" +
|
||||
"shareKey='" + shareKey + '\'' +
|
||||
", type='" + type + '\'' +
|
||||
", sharePassword='" + sharePassword + '\'' +
|
||||
", shareUrl='" + shareUrl + '\'' +
|
||||
", standardUrl='" + standardUrl + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,62 +1,31 @@
|
||||
package cn.qaiu.parser;//package cn.qaiu.lz.common.parser;
|
||||
|
||||
import cn.qaiu.parser.impl.*;
|
||||
import io.vertx.core.Future;
|
||||
|
||||
public interface IPanTool {
|
||||
Future<String> parse();
|
||||
|
||||
// 基于枚举PanDomainTemplate匹配分享链接类型
|
||||
static IPanTool typeMatching(String type, String key, String pwd) {
|
||||
return switch (type) {
|
||||
case "lz" -> new LzTool(key, pwd);
|
||||
case "cow" -> new CowTool(key, pwd);
|
||||
case "ec" -> new EcTool(key, pwd);
|
||||
case "fc" -> new FcTool(key, pwd);
|
||||
case "uc" -> new UcTool(key, pwd);
|
||||
case "ye" -> new YeTool(key, pwd);
|
||||
case "fj" -> new FjTool(key, pwd);
|
||||
case "qk" -> new QkTool(key, pwd);
|
||||
case "le" -> new LeTool(key, pwd);
|
||||
case "ws" -> new WsTool(key, pwd);
|
||||
case "qq" -> new QQTool(key, pwd);
|
||||
case "iz" -> new IzTool(key, pwd);
|
||||
case "ce" -> new CeTool(key, pwd);
|
||||
default -> {
|
||||
throw new UnsupportedOperationException("未知分享类型");
|
||||
}
|
||||
};
|
||||
try {
|
||||
return PanDomainTemplate
|
||||
.fromShortName(type)
|
||||
.generateShareLink(key)
|
||||
.setShareLinkInfoPwd(pwd)
|
||||
.createTool();
|
||||
} catch (Exception e) {
|
||||
throw new UnsupportedOperationException("未知分享类型", e);
|
||||
}
|
||||
}
|
||||
|
||||
static IPanTool shareURLPrefixMatching(String url, String pwd) {
|
||||
|
||||
if (url.contains(CowTool.LINK_KEY)) {
|
||||
return new CowTool(url, pwd);
|
||||
} else if (url.startsWith(EcTool.SHARE_URL_PREFIX)) {
|
||||
return new EcTool(url, pwd);
|
||||
} else if (url.startsWith(FcTool.SHARE_URL_PREFIX0)) {
|
||||
return new FcTool(url, pwd);
|
||||
} else if (url.startsWith(UcTool.SHARE_URL_PREFIX)) {
|
||||
return new UcTool(url, pwd);
|
||||
} else if (url.startsWith(YeTool.SHARE_URL_PREFIX)) {
|
||||
return new YeTool(url, pwd);
|
||||
} else if (url.startsWith(FjTool.SHARE_URL_PREFIX) || url.startsWith(FjTool.SHARE_URL_PREFIX2)) {
|
||||
return new FjTool(url, pwd);
|
||||
} else if (url.startsWith(IzTool.SHARE_URL_PREFIX)) {
|
||||
return new IzTool(url, pwd);
|
||||
} else if (url.contains(LzTool.LINK_KEY)) {
|
||||
return new LzTool(url, pwd);
|
||||
} else if (url.startsWith(LeTool.SHARE_URL_PREFIX)) {
|
||||
return new LeTool(url, pwd);
|
||||
} else if (url.contains(WsTool.SHARE_URL_PREFIX) || url.contains(WsTool.SHARE_URL_PREFIX2)) {
|
||||
return new WsTool(url, pwd);
|
||||
} else if (url.contains(QQTool.SHARE_URL_PREFIX)) {
|
||||
return new QQTool(url, pwd);
|
||||
} else if (url.contains("/s/")) {
|
||||
// Cloudreve 网盘通用解析
|
||||
return new CeTool(url, pwd);
|
||||
try {
|
||||
return PanDomainTemplate
|
||||
.fromShareUrl(url)
|
||||
.setShareLinkInfoPwd(pwd)
|
||||
.createTool();
|
||||
} catch (Exception e) {
|
||||
throw new UnsupportedOperationException("未知分享类型", e);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("未知分享类型");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import cn.qaiu.WebClientVertxInit;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
@@ -35,16 +35,7 @@ public abstract class PanBase {
|
||||
protected WebClient clientNoRedirects = WebClient.create(WebClientVertxInit.get(),
|
||||
new WebClientOptions().setFollowRedirects(false));
|
||||
|
||||
/**
|
||||
* 分享key 可以是整个URL; 如果是URL实现该类时要
|
||||
* 使用{@link CommonUtils#adaptShortPaths(String urlPrefix, String key)}获取真实的分享key
|
||||
*/
|
||||
protected String key;
|
||||
|
||||
/**
|
||||
* 分享密码
|
||||
*/
|
||||
protected String pwd;
|
||||
protected ShareLinkInfo shareLinkInfo;
|
||||
|
||||
/**
|
||||
* 子类重写此构造方法不需要添加额外逻辑
|
||||
@@ -55,12 +46,9 @@ public abstract class PanBase {
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param key 分享key/url
|
||||
* @param pwd 分享密码
|
||||
*/
|
||||
protected PanBase(String key, String pwd) {
|
||||
this.key = key;
|
||||
this.pwd = pwd;
|
||||
public PanBase(ShareLinkInfo shareLinkInfo) {
|
||||
this.shareLinkInfo = shareLinkInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
225
parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java
Normal file
225
parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java
Normal file
@@ -0,0 +1,225 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.impl.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 枚举类 PanDomainTemplate 定义了不同网盘服务的模板信息,包括:
|
||||
* <ul>
|
||||
* <li>displayName: 网盘服务的显示名称,用于用户界面展示。</li>
|
||||
* <li>shortName: 网盘服务的简称,用于内部逻辑处理,例如API路径。</li>
|
||||
* <li>regexPattern: 用于匹配和解析分享链接的正则表达式。</li>
|
||||
* <li>standardUrlTemplate: 网盘服务的标准URL模板,用于规范化分享链接。</li>
|
||||
* <li>toolClass: 网盘解析工具实现类。</li>
|
||||
* </ul>
|
||||
* 该类提供方法来解析和规范化不同来源的分享链接,确保它们可以转换为统一的标准链接格式。
|
||||
* 通过这种方式,应用程序可以更容易地处理和识别不同网盘服务的分享链接.
|
||||
*
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* at 2023/6/13 4:26
|
||||
*/
|
||||
public enum PanDomainTemplate {
|
||||
|
||||
// 网盘定义
|
||||
LZ("蓝奏云",
|
||||
"lz",
|
||||
"https://([a-z]+)?\\.?lanzou[a-z]\\.com/(.+/)?(.+)",
|
||||
"https://lanzoux.com/{shareKey}",
|
||||
LzTool.class),
|
||||
|
||||
// https://www.feijix.com/s/
|
||||
// https://share.feijipan.com/s/
|
||||
FJ("小飞机网盘",
|
||||
"fj",
|
||||
"https://(share\\.feijipan\\.com|www\\.feijix\\.com)/s/(.+)",
|
||||
"https://www.feijix.com/s/{shareKey}",
|
||||
FjTool.class),
|
||||
|
||||
// https://lecloud.lenovo.com/share/
|
||||
LE("联想乐云",
|
||||
"le",
|
||||
"https://lecloud?\\.lenovo\\.com/share/(.+)",
|
||||
"https://lecloud.lenovo.com/share/{shareKey}",
|
||||
LeTool.class),
|
||||
|
||||
// https://v2.fangcloud.com/s/
|
||||
FC("亿方云",
|
||||
"fc",
|
||||
"https://v2\\.fangcloud\\.(com|cn)/(s|sharing)/([^/]+)",
|
||||
"https://v2.fangcloud.com/s/{shareKey}",
|
||||
FcTool.class),
|
||||
// https://www.ilanzou.com/s/
|
||||
IZ("蓝奏云优享",
|
||||
"iz",
|
||||
"https://www\\.ilanzou\\.com/s/(.+)",
|
||||
"https://www.ilanzou.com/s/{shareKey}",
|
||||
IzTool.class),
|
||||
// https://wx.mail.qq.com/ftn/download?
|
||||
QQ("QQ邮箱中转站",
|
||||
"qq",
|
||||
"https://i?wx\\.mail\\.qq\\.com/ftn/download\\?(.+)",
|
||||
"https://iwx.mail.qq.com/ftn/download/{shareKey}",
|
||||
QQTool.class),
|
||||
// https://f.ws59.cn/f/或者https://www.wenshushu.cn/f/
|
||||
WS("文叔叔",
|
||||
"ws",
|
||||
"https://(f\\.ws59\\.cn|www\\.wenshushu\\.cn)/f/(.+)",
|
||||
"https://f.ws59.cn/f/{shareKey}",
|
||||
WsTool.class),
|
||||
// https://www.123pan.com/s/
|
||||
YE("123网盘",
|
||||
"ye",
|
||||
"https://www\\.123pan\\.com/s/(.+)\\.html",
|
||||
"https://www.123pan.com/s/{shareKey}.html",
|
||||
YeTool.class),
|
||||
// https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={code}&isShare=1
|
||||
EC("移动云空间",
|
||||
"ec",
|
||||
"https://www\\.ecpan\\.cn/web(/%23|/#)?/yunpanProxy\\?path=.*&data=" +
|
||||
"([^&]+)&isShare=1",
|
||||
"https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={shareKey}&isShare=1",
|
||||
EcTool.class),
|
||||
// https://cowtransfer.com/s/
|
||||
COW("奶牛快传",
|
||||
"cow",
|
||||
"https://(.*)cowtransfer\\.com/s/(.+)",
|
||||
"https://cowtransfer.com/s/{shareKey}",
|
||||
CowTool.class),
|
||||
// https://pan.huang1111.cn/s/
|
||||
CE("huang1111",
|
||||
"ce",
|
||||
"https://pan\\.huang1111\\.cn/s/(.+)",
|
||||
"https://pan.huang1111.cn/s/{shareKey}",
|
||||
CeTool.class);
|
||||
|
||||
|
||||
// 网盘的显示名称,用于用户界面显示
|
||||
private final String displayName;
|
||||
|
||||
// 网盘的简短名称,用于内部逻辑处理,如REST API路径
|
||||
private final String shortName;
|
||||
|
||||
// 用于匹配和解析分享链接的正则表达式
|
||||
private final String regexPattern;
|
||||
|
||||
// 网盘的标准链接模板,不含占位符,用于规范化分享链接
|
||||
private final String standardUrlTemplate;
|
||||
private final ShareLinkInfo shareLinkInfo;
|
||||
// 指向IPanTool实现类
|
||||
private final Class<? extends IPanTool> toolClass;
|
||||
|
||||
PanDomainTemplate(String displayName, String shortName, String regexPattern,
|
||||
String standardUrlTemplate, Class<? extends IPanTool> toolClass) {
|
||||
this.displayName = displayName;
|
||||
this.shortName = shortName;
|
||||
this.regexPattern = regexPattern;
|
||||
this.standardUrlTemplate = standardUrlTemplate;
|
||||
this.toolClass = toolClass;
|
||||
this.shareLinkInfo = ShareLinkInfo.newBuilder().type(shortName).build();
|
||||
}
|
||||
|
||||
|
||||
// 解析并规范化分享链接
|
||||
synchronized public PanDomainTemplate normalizeShareLink() {
|
||||
if (shareLinkInfo == null) {
|
||||
throw new IllegalArgumentException("ShareLinkInfo not init");
|
||||
}
|
||||
// 匹配并提取shareKey
|
||||
String shareUrl = shareLinkInfo.getShareUrl();
|
||||
if (StringUtils.isEmpty(shareUrl)) {
|
||||
throw new IllegalArgumentException("ShareLinkInfo shareUrl is empty");
|
||||
}
|
||||
Pattern pattern = Pattern.compile(regexPattern);
|
||||
Matcher matcher = pattern.matcher(shareUrl);
|
||||
if (matcher.find()) {
|
||||
String shareKey = matcher.group(matcher.groupCount());
|
||||
// 返回规范化的标准链接
|
||||
String standardUrl = standardUrlTemplate.replace("{shareKey}", shareKey);
|
||||
shareLinkInfo.setShareUrl(shareUrl);
|
||||
shareLinkInfo.setShareKey(shareKey);
|
||||
shareLinkInfo.setStandardUrl(standardUrl);
|
||||
return this;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid share URL for " + displayName);
|
||||
}
|
||||
|
||||
public IPanTool createTool() {
|
||||
if (shareLinkInfo == null || StringUtils.isEmpty(shareLinkInfo.getType())) {
|
||||
throw new IllegalArgumentException("ShareLinkInfo not init or type is empty");
|
||||
}
|
||||
if (StringUtils.isEmpty(shareLinkInfo.getShareKey())) {
|
||||
this.normalizeShareLink();
|
||||
}
|
||||
try {
|
||||
return toolClass
|
||||
.getDeclaredConstructor(ShareLinkInfo.class)
|
||||
.newInstance(shareLinkInfo);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("无法创建工具实例: " + toolClass.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成分享链接的方法
|
||||
synchronized public PanDomainTemplate generateShareLink(String shareKey) {
|
||||
shareLinkInfo.setShareKey(shareKey);
|
||||
shareLinkInfo.setStandardUrl(standardUrlTemplate.replace("{shareKey}", shareKey));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public String getDisplayName() {
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
public String getRegexPattern() {
|
||||
return regexPattern;
|
||||
}
|
||||
|
||||
public String getStandardUrlTemplate() {
|
||||
return standardUrlTemplate;
|
||||
}
|
||||
|
||||
|
||||
public ShareLinkInfo getShareLinkInfo() {
|
||||
return shareLinkInfo;
|
||||
}
|
||||
|
||||
synchronized public PanDomainTemplate setShareLinkInfoPwd(String pwd) {
|
||||
shareLinkInfo.setSharePassword(pwd);
|
||||
return this;
|
||||
}
|
||||
|
||||
synchronized public PanDomainTemplate setShareLinkInfoUrl(String pwd) {
|
||||
shareLinkInfo.setSharePassword(pwd);
|
||||
return this;
|
||||
}
|
||||
|
||||
// 根据分享链接获取PanDomainTemplate实例
|
||||
synchronized public static PanDomainTemplate fromShareUrl(String shareUrl) {
|
||||
for (PanDomainTemplate template : values()) {
|
||||
if (shareUrl.matches(template.regexPattern)) {
|
||||
template.getShareLinkInfo().setShareUrl(shareUrl);
|
||||
return template.normalizeShareLink();
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported share URL");
|
||||
}
|
||||
|
||||
// 根据shortName获取枚举实例
|
||||
public static PanDomainTemplate fromShortName(String shortName) {
|
||||
try {
|
||||
return Enum.valueOf(PanDomainTemplate.class, shortName.toUpperCase());
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
// 如果没有找到对应的枚举实例,抛出异常
|
||||
throw new IllegalArgumentException("No enum constant for short name: " + shortName);
|
||||
}
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import io.vertx.core.Future;
|
||||
@@ -7,9 +8,6 @@ import io.vertx.core.buffer.Buffer;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.HttpRequest;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* <a href="https://github.com/cloudreve/Cloudreve">Cloudreve网盘解析</a> <br>
|
||||
* <a href="https://pan.xiaomuxi.cn">暮希云盘</a> <br>
|
||||
@@ -17,34 +15,40 @@ import java.net.URL;
|
||||
*/
|
||||
public class CeTool extends PanBase implements IPanTool {
|
||||
|
||||
private static final String DOWNLOAD_API_PATH = "/api/v3/share/download/";
|
||||
private static final String DOWNLOAD_API_PATH = "https://pan.huang1111.cn/api/v3/share/download/";
|
||||
|
||||
// api/v3/share/info/g31PcQ?password=qaiu
|
||||
private static final String SHARE_API_PATH = "/api/v3/share/info/";
|
||||
private static final String SHARE_API_PATH = "https://pan.huang1111.cn/api/v3/share/info/";
|
||||
|
||||
public CeTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public CeTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
|
||||
public Future<String> parse() {
|
||||
String key = shareLinkInfo.getShareKey();
|
||||
String pwd = shareLinkInfo.getSharePassword();
|
||||
// https://pan.huang1111.cn/s/wDz5TK
|
||||
// https://pan.huang1111.cn/s/y12bI6 -> https://pan.huang1111
|
||||
// .cn/api/v3/share/download/y12bI6?path=undefined%2Fundefined;
|
||||
// 类型解析 -> /ce/https_pan.huang1111.cn_s_wDz5TK
|
||||
// parser接口 -> /parser?url=https://pan.huang1111.cn/s/wDz5TK
|
||||
try {
|
||||
if (key.startsWith("https_") || key.startsWith("http_")) {
|
||||
key = key.replace("https_", "https://")
|
||||
.replace("http_", "http://")
|
||||
.replace("_", "/");
|
||||
}
|
||||
// 处理URL
|
||||
URL url = new URL(key);
|
||||
String path = url.getPath();
|
||||
String shareKey = path.substring(3);
|
||||
String downloadApiUrl = url.getProtocol() + "://" + url.getHost() + DOWNLOAD_API_PATH + shareKey + "?path" +
|
||||
"=undefined/undefined;";
|
||||
String shareApiUrl = url.getProtocol() + "://" + url.getHost() + SHARE_API_PATH + shareKey;
|
||||
// if (key.startsWith("https_") || key.startsWith("http_")) {
|
||||
// key = key.replace("https_", "https://")
|
||||
// .replace("http_", "http://")
|
||||
// .replace("_", "/");
|
||||
// }
|
||||
// // 处理URL
|
||||
// URL url = new URL(key);
|
||||
// String path = url.getPath();
|
||||
// String shareKey = path.substring(3);
|
||||
// String downloadApiUrl = url.getProtocol() + "://" + url.getHost() + DOWNLOAD_API_PATH + shareKey + "?path" +
|
||||
// "=undefined/undefined;";
|
||||
// String shareApiUrl = url.getProtocol() + "://" + url.getHost() + SHARE_API_PATH + shareKey;
|
||||
|
||||
var shareApiUrl = SHARE_API_PATH;
|
||||
var downloadApiUrl = DOWNLOAD_API_PATH;
|
||||
|
||||
// 设置cookie
|
||||
HttpRequest<Buffer> httpRequest = clientSession.getAbs(shareApiUrl);
|
||||
@@ -53,7 +57,7 @@ public class CeTool extends PanBase implements IPanTool {
|
||||
}
|
||||
// 获取下载链接
|
||||
httpRequest.send().onSuccess(res -> getDownURL(downloadApiUrl)).onFailure(handleFail(shareApiUrl));
|
||||
} catch (MalformedURLException e) {
|
||||
} catch (Exception e) {
|
||||
fail(e, "URL解析错误");
|
||||
}
|
||||
return promise.future();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -16,16 +16,14 @@ import org.apache.commons.lang3.StringUtils;
|
||||
public class CowTool extends PanBase implements IPanTool {
|
||||
|
||||
private static final String API_REQUEST_URL = "https://cowtransfer.com/core/api/transfer/share";
|
||||
public static final String SHARE_URL_PREFIX = "https://cowtransfer.com/s/";
|
||||
|
||||
public static final String LINK_KEY = "cowtransfer.com/s/";
|
||||
|
||||
public CowTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public CowTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
|
||||
public Future<String> parse() {
|
||||
key = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
final String key = shareLinkInfo.getShareKey();
|
||||
String url = API_REQUEST_URL + "?uniqueUrl=" + key;
|
||||
client.getAbs(url).send().onSuccess(res -> {
|
||||
JsonObject resJson = res.bodyAsJsonObject();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
@@ -12,20 +12,22 @@ import io.vertx.uritemplate.UriTemplate;
|
||||
* 移动云空间解析
|
||||
*/
|
||||
public class EcTool extends PanBase implements IPanTool {
|
||||
// https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1
|
||||
private static final String FIRST_REQUEST_URL = "https://www.ecpan.cn/drive/fileextoverrid" +
|
||||
".do?extractionCode={extractionCode}&chainUrlTemplate=https:%2F%2Fwww.ecpan" +
|
||||
".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 EcTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public EcTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
|
||||
public Future<String> parse() {
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
|
||||
// 第一次请求 获取文件信息
|
||||
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL))
|
||||
.setTemplateParam("dataKey", dataKey)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.Promise;
|
||||
@@ -21,19 +21,19 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class FcTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX0 = "https://v2.fangcloud.com/s";
|
||||
public static final String SHARE_URL_PREFIX = "https://v2.fangcloud.com/sharing/";
|
||||
public static final String SHARE_URL_PREFIX2 = "https://v2.fangcloud.cn/sharing/";
|
||||
private static final String DOWN_REQUEST_URL = "https://v2.fangcloud.cn/apps/files/download?file_id={fid}" +
|
||||
"&scenario=share&unique_name={uname}";
|
||||
|
||||
public FcTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public FcTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
|
||||
public Future<String> parse() {
|
||||
String data = key.replace("share","sharing");
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
WebClientSession sClient = WebClientSession.create(client);
|
||||
// 第一次请求 自动重定向
|
||||
sClient.getAbs(SHARE_URL_PREFIX + dataKey).send().onSuccess(res -> {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.AESUtils;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import cn.qaiu.util.UUIDUtil;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
@@ -18,10 +18,7 @@ import io.vertx.uritemplate.UriTemplate;
|
||||
* @version V016_230609
|
||||
*/
|
||||
public class FjTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://www.feijix.com/s/";
|
||||
public static final String REFERER_URL = "https://share.feijipan.com/";
|
||||
public static final String SHARE_URL_PREFIX2 = REFERER_URL + "s/";
|
||||
private static final String API_URL_PREFIX = "https://api.feijipan.com/ws/";
|
||||
|
||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" +
|
||||
@@ -38,19 +35,14 @@ public class FjTool extends PanBase implements IPanTool {
|
||||
"={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);
|
||||
public FjTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
String dataKey;
|
||||
if (key.startsWith(SHARE_URL_PREFIX2)) {
|
||||
dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX2, key);
|
||||
} else {
|
||||
dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
}
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
|
||||
// 240530 此处shareId又改为了原始的shareId, nm玩呢?
|
||||
// 240530 此处shareId又改为了原始的shareId
|
||||
String shareId = dataKey; // String.valueOf(AESUtils.idEncrypt(dataKey));
|
||||
long nowTs = System.currentTimeMillis();
|
||||
String tsEncode = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.AESUtils;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
@@ -26,12 +26,12 @@ public class IzTool extends PanBase implements IPanTool {
|
||||
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "file/redirect?downloadId={fidEncode}&enable=1" +
|
||||
"&devType=6&uuid={uuid}×tamp={ts}&auth={auth}";
|
||||
|
||||
public IzTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public IzTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
String dataKey = shareLinkInfo.getShareKey();
|
||||
|
||||
// 24.5.12 ilanzou改规则无需计算shareId
|
||||
// String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey));
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
@@ -13,16 +13,15 @@ import java.util.UUID;
|
||||
* <a href="https://lecloud.lenovo.com/">联想乐云</a>
|
||||
*/
|
||||
public class LeTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://lecloud.lenovo.com/share/";
|
||||
private static final String API_URL_PREFIX = "https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/";
|
||||
|
||||
public LeTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public LeTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
// {"shareId":"xxx","password":"xxx","directoryId":"-1"}
|
||||
String apiUrl1 = API_URL_PREFIX + "shareInfo";
|
||||
client.postAbs(apiUrl1)
|
||||
@@ -47,7 +46,7 @@ public class LeTool extends PanBase implements IPanTool {
|
||||
JsonObject fileInfoJson = files.getJsonObject(0);
|
||||
if (fileInfoJson != null) {
|
||||
// TODO 文件大小fileSize和文件名fileName
|
||||
Long fileId = fileInfoJson.getLong("fileId");
|
||||
String fileId = fileInfoJson.getString("fileId");
|
||||
// 根据文件ID获取跳转链接
|
||||
getDownURL(dataKey, fileId);
|
||||
}
|
||||
@@ -61,7 +60,7 @@ public class LeTool extends PanBase implements IPanTool {
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private void getDownURL(String key, Long fileId) {
|
||||
private void getDownURL(String key, String fileId) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
JsonArray fileIds = JsonArray.of(fileId);
|
||||
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.JsExecUtils;
|
||||
@@ -23,15 +24,15 @@ public class LzTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://wwwa.lanzoux.com";
|
||||
|
||||
public static final String LINK_KEY = "lanzou";
|
||||
|
||||
public LzTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public LzTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Future<String> parse() {
|
||||
String sUrl = key.startsWith("https://") ? key : SHARE_URL_PREFIX + "/" + key;
|
||||
String sUrl = shareLinkInfo.getStandardUrl();
|
||||
String pwd = shareLinkInfo.getSharePassword();
|
||||
|
||||
WebClient client = clientNoRedirects;
|
||||
client.getAbs(sUrl).send().onSuccess(res -> {
|
||||
@@ -50,34 +51,37 @@ public class LzTool extends PanBase implements IPanTool {
|
||||
}
|
||||
|
||||
jsText = jsText.replace("document.getElementById('pwd').value", "\"" + pwd + "\"");
|
||||
jsText = jsText.substring(0, jsText.indexOf("document.getElementById('rpt')"));
|
||||
int i = jsText.indexOf("document.getElementById('rpt')");
|
||||
if (i > 0) {
|
||||
jsText = jsText.substring(0, i);
|
||||
}
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "down_p");
|
||||
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
String iframePath = matcher.group(1);
|
||||
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
|
||||
String html2 = res2.bodyAsString();
|
||||
} else {
|
||||
// 没有密码
|
||||
String iframePath = matcher.group(1);
|
||||
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
|
||||
String html2 = res2.bodyAsString();
|
||||
|
||||
// 去TMD正则
|
||||
// Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
|
||||
String jsText = getJsText(html2);
|
||||
if (jsText == null) {
|
||||
fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": js脚本匹配失败, 可能分享已失效");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, null);
|
||||
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
}
|
||||
}).onFailure(handleFail(SHARE_URL_PREFIX));
|
||||
// 去TMD正则
|
||||
// Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
|
||||
String jsText = getJsText(html2);
|
||||
if (jsText == null) {
|
||||
fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": js脚本匹配失败, 可能分享已失效");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, null);
|
||||
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
fail(e, "js引擎执行失败");
|
||||
}
|
||||
}).onFailure(handleFail(SHARE_URL_PREFIX));
|
||||
}
|
||||
}).onFailure(handleFail(sUrl));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@@ -1,43 +1,48 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.util.StringUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.StringUtils;
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <a href="https://wx.mail.qq.com/">QQ邮箱</a>
|
||||
*/
|
||||
public class QQTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "wx.mail.qq.com/ftn/download?";
|
||||
public static final String REDIRECT_URL_TEMP = "https://iwx.mail.qq.com/ftn/download?func=4&key={key}&code={code}";
|
||||
|
||||
public QQTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public QQTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Future<String> parse() {
|
||||
|
||||
WebClient httpClient = this.client;
|
||||
|
||||
// 补全链接
|
||||
if (!this.key.startsWith("https://" + SHARE_URL_PREFIX)) {
|
||||
if (this.key.startsWith(SHARE_URL_PREFIX)) {
|
||||
this.key = "https://" + this.key;
|
||||
} else if (this.key.startsWith("func=")) {
|
||||
this.key = "https://" + SHARE_URL_PREFIX + this.key;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("未知分享类型");
|
||||
}
|
||||
// QQ mail 直接替换为302链接 无需请求
|
||||
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(shareLinkInfo.getShareUrl(), StandardCharsets.UTF_8);
|
||||
Map<String, List<String>> prms = queryStringDecoder.parameters();
|
||||
if (prms.containsKey("key") && prms.containsKey("code") && prms.containsKey("func")) {
|
||||
log.info(prms.get("func").get(0));
|
||||
promise.complete(REDIRECT_URL_TEMP.replace("{key}",
|
||||
prms.get("key").get(0)).replace("{code}", prms.get("code").get(0)));
|
||||
} else {
|
||||
fail("key 不合法");
|
||||
}
|
||||
|
||||
|
||||
// 通过请求URL获取文件信息和直链 暂时不需要
|
||||
// getFileInfo(key);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private void getFileInfo(String key) {
|
||||
// 设置基础HTTP头部
|
||||
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
|
||||
"like " +
|
||||
@@ -50,7 +55,7 @@ public class QQTool extends PanBase implements IPanTool {
|
||||
headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile");
|
||||
|
||||
// 获取下载中转站页面
|
||||
httpClient.getAbs(this.key).putHeaders(headers).send().onSuccess(res -> {
|
||||
client.getAbs(key).putHeaders(headers).send().onSuccess(res -> {
|
||||
if (res.statusCode() == 200) {
|
||||
String html = res.bodyAsString();
|
||||
|
||||
@@ -61,10 +66,10 @@ public class QQTool extends PanBase implements IPanTool {
|
||||
|
||||
if (filename != null && filesize != null && fileurl != null) {
|
||||
// 设置所需HTTP头部
|
||||
headers.set("Referer", "https://" + StringUtils.StringCutNot(this.key, "https://", "/") + "/");
|
||||
headers.set("Referer", "https://" + StringUtils.StringCutNot(key, "https://", "/") + "/");
|
||||
headers.set("Host", StringUtils.StringCutNot(fileurl, "https://", "/"));
|
||||
res.headers().forEach((k, v) -> {
|
||||
if (k.toLowerCase().equals("set-cookie")) {
|
||||
if (k.equalsIgnoreCase("set-cookie")) {
|
||||
headers.set("Cookie", "mail5k=" + StringUtils.StringCutNot(v, "mail5k=", ";") + ";");
|
||||
}
|
||||
});
|
||||
@@ -82,9 +87,7 @@ public class QQTool extends PanBase implements IPanTool {
|
||||
} else {
|
||||
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
||||
}
|
||||
}).onFailure(this.handleFail(this.key));
|
||||
|
||||
return promise.future();
|
||||
}).onFailure(this.handleFail(key));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import io.vertx.core.Future;
|
||||
@@ -9,11 +10,14 @@ import java.util.stream.IntStream;
|
||||
|
||||
public class QkTool extends PanBase implements IPanTool {
|
||||
|
||||
public QkTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public QkTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
final String key = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
|
||||
promise.complete("https://lz.qaiu.top");
|
||||
IntStream.range(0, 1000).forEach(num -> {
|
||||
clientNoRedirects.getAbs(key).send()
|
||||
@@ -33,14 +37,6 @@ public class QkTool extends PanBase implements IPanTool {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
new QkTool("https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12298704&dk" +
|
||||
"=19ab590770399d4438ea885446e27186cc668cdfa559f5fcc063a1ecf78008e5&pk" +
|
||||
"=ef45aa4d25c1dcecb631b3394f51539d59cb35c6a40c3911df8ba431ba2a3244&pc=true&ot=ali&ob=sync-cloud-disk" +
|
||||
"&ok=649593714557087744.dex&fn=classes" +
|
||||
".dex&ds=8909208&dc=1&bi=asdddsad&ri=&ts=1701235051759&sn" +
|
||||
"=13dc33749bd9cc108009fa505b3ecca9f358d70874352858475956ba4240e4c3", "")
|
||||
.parse().onSuccess((res) -> {
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
/**
|
||||
* UC网盘解析
|
||||
*/
|
||||
public class UcTool extends PanBase implements IPanTool {
|
||||
private static final String API_URL_PREFIX = "https://pc-api.uc.cn/1/clouddrive/";
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "https://fast.uc.cn/s/";
|
||||
|
||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "share/sharepage/token?entry=ft&fr=pc&pr" +
|
||||
"=UCBrowser";
|
||||
|
||||
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "transfer_share/detail?pwd_id={pwd_id}&passcode" +
|
||||
"={passcode}&stoken={stoken}";
|
||||
|
||||
private static final String THIRD_REQUEST_URL = API_URL_PREFIX + "file/download?entry=ft&fr=pc&pr=UCBrowser";
|
||||
|
||||
public UcTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
var dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
var passcode = (pwd == null) ? "" : pwd;
|
||||
var jsonObject = JsonObject.of("share_for_transfer", true);
|
||||
jsonObject.put("pwd_id", dataKey);
|
||||
jsonObject.put("passcode", passcode);
|
||||
// 第一次请求 获取文件信息
|
||||
client.postAbs(FIRST_REQUEST_URL).sendJsonObject(jsonObject).onSuccess(res -> {
|
||||
log.debug("第一阶段 {}", res.body());
|
||||
var resJson = res.bodyAsJsonObject();
|
||||
if (resJson.getInteger("code") != 0) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||
return;
|
||||
}
|
||||
var stoken = resJson.getJsonObject("data").getString("stoken");
|
||||
// 第二次请求
|
||||
client.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||
.setTemplateParam("pwd_id", dataKey)
|
||||
.setTemplateParam("passcode", passcode)
|
||||
.setTemplateParam("stoken", stoken)
|
||||
.send().onSuccess(res2 -> {
|
||||
log.debug("第二阶段 {}", res2.body());
|
||||
JsonObject resJson2 = res2.bodyAsJsonObject();
|
||||
if (resJson2.getInteger("code") != 0) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson2);
|
||||
return;
|
||||
}
|
||||
// 文件信息
|
||||
var info = resJson2.getJsonObject("data").getJsonArray("list").getJsonObject(0);
|
||||
// 第二次请求
|
||||
var bodyJson = JsonObject.of()
|
||||
.put("fids", JsonArray.of(info.getString("fid")))
|
||||
.put("pwd_id", dataKey)
|
||||
.put("stoken", stoken)
|
||||
.put("fids_token", JsonArray.of(info.getString("share_fid_token")));
|
||||
client.postAbs(THIRD_REQUEST_URL).sendJsonObject(bodyJson)
|
||||
.onSuccess(res3 -> {
|
||||
log.debug("第三阶段 {}", res3.body());
|
||||
var resJson3 = res3.bodyAsJsonObject();
|
||||
if (resJson3.getInteger("code") != 0) {
|
||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson2);
|
||||
return;
|
||||
}
|
||||
promise.complete(resJson3.getJsonArray("data").getJsonObject(0).getString("download_url"));
|
||||
}).onFailure(handleFail(THIRD_REQUEST_URL));
|
||||
|
||||
}).onFailure(handleFail(SECOND_REQUEST_URL));
|
||||
}
|
||||
).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||
return promise.future();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.util.StringUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import io.vertx.core.Future;
|
||||
@@ -13,33 +9,27 @@ import io.vertx.core.json.DecodeException;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* <a href="https://www.wenshushu.cn/">文叔叔</a>
|
||||
*/
|
||||
public class WsTool extends PanBase implements IPanTool {
|
||||
|
||||
public static final String SHARE_URL_PREFIX = "www.wenshushu.cn/f/";
|
||||
public static final String SHARE_URL_PREFIX2 = "f.ws59.cn/f/";
|
||||
public static final String SHARE_URL_API = "https://www.wenshushu.cn/ap/";
|
||||
|
||||
public WsTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public WsTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
|
||||
WebClient httpClient = this.client;
|
||||
final String key = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
|
||||
// 补全链接
|
||||
if (!this.key.startsWith("https://" + SHARE_URL_PREFIX) && !this.key.startsWith("https://" + SHARE_URL_PREFIX2)) {
|
||||
if (this.key.startsWith(SHARE_URL_PREFIX) || this.key.startsWith(SHARE_URL_PREFIX2)) {
|
||||
this.key = "https://" + this.key;
|
||||
} else if (this.key.matches("^[A-Za-z0-9]+$")) {
|
||||
this.key = "https://" + SHARE_URL_PREFIX + this.key;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("未知分享类型");
|
||||
}
|
||||
}
|
||||
|
||||
// 设置基础HTTP头部
|
||||
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
|
||||
@@ -66,8 +56,8 @@ public class WsTool extends PanBase implements IPanTool {
|
||||
// 获取文件夹信息
|
||||
httpClient.postAbs(SHARE_URL_API + "task/mgrtask").putHeaders(headers)
|
||||
.sendJsonObject(JsonObject.of(
|
||||
"tid", StringUtils.StringCutNot(key, this.key.startsWith(SHARE_URL_PREFIX) ? SHARE_URL_PREFIX : SHARE_URL_PREFIX2),
|
||||
"password", ""
|
||||
"tid", key,
|
||||
"password", pwd
|
||||
)).onSuccess(res2 -> {
|
||||
|
||||
if (res2.statusCode() == 200) {
|
||||
@@ -128,16 +118,9 @@ public class WsTool extends PanBase implements IPanTool {
|
||||
// 调试输出文件直链
|
||||
System.out.println("文件直链: " + fileurl);
|
||||
|
||||
if (!fileurl.equals(""))
|
||||
{
|
||||
try {
|
||||
promise.complete(URLDecoder.decode(fileurl, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
promise.complete(fileurl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!fileurl.equals("")) {
|
||||
promise.complete(URLDecoder.decode(fileurl, StandardCharsets.UTF_8));
|
||||
} else {
|
||||
this.fail("文件已失效");
|
||||
}
|
||||
|
||||
@@ -166,7 +149,7 @@ public class WsTool extends PanBase implements IPanTool {
|
||||
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
||||
}
|
||||
|
||||
}).onFailure(this.handleFail(this.key));
|
||||
}).onFailure(this.handleFail(key));
|
||||
|
||||
} catch (DecodeException | NullPointerException e) {
|
||||
this.fail("token获取失败,可能是分享链接的方式已更新");
|
||||
@@ -175,7 +158,7 @@ public class WsTool extends PanBase implements IPanTool {
|
||||
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
||||
}
|
||||
|
||||
}).onFailure(this.handleFail(this.key));
|
||||
}).onFailure(this.handleFail(key));
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
@@ -30,13 +31,14 @@ public class YeTool extends PanBase implements IPanTool {
|
||||
"&shareKey={shareKey}&SharePwd={pwd}&ParentFileId=0&Page=1&event=homeListFile&operateType=1";
|
||||
private static final String DOWNLOAD_API_URL = "https://www.123pan.com/a/api/share/download/info?{authK}={authV}";
|
||||
|
||||
public YeTool(String key, String pwd) {
|
||||
super(key, pwd);
|
||||
public YeTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
|
||||
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||
final String dataKey = shareLinkInfo.getShareKey();
|
||||
final String pwd = shareLinkInfo.getSharePassword();
|
||||
|
||||
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("key", dataKey).send().onSuccess(res -> {
|
||||
|
||||
|
||||
37
parser/src/test/java/cn/qaiu/parser/FCURLParser.java
Normal file
37
parser/src/test/java/cn/qaiu/parser/FCURLParser.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FCURLParser {// 定义前缀
|
||||
public static final String SHARE_URL_PREFIX0 = "https://v2.fangcloud.com/s";
|
||||
public static final String SHARE_URL_PREFIX = "https://v2.fangcloud.com/sharing/";
|
||||
public static final String SHARE_URL_PREFIX2 = "https://v2.fangcloud.cn/sharing/";
|
||||
|
||||
// 定义正则表达式,适用于所有前缀
|
||||
private static final String SHARING_REGEX = "https://www\\.ecpan\\.cn/web(/%23|/#)?/yunpanProxy\\?path=.*&data=" +
|
||||
"([^&]+)&isShare=1";
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 测试 URL
|
||||
String[] urls = {
|
||||
"https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1",
|
||||
"https://www.ecpan.cn/web/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1",
|
||||
"https://v2.fangcloud.cn/sharing/xyz789"
|
||||
};
|
||||
|
||||
// 编译正则表达式
|
||||
Pattern pattern = Pattern.compile(SHARING_REGEX);
|
||||
|
||||
for (String url : urls) {
|
||||
Matcher matcher = pattern.matcher(url);
|
||||
if (matcher.find()) {
|
||||
System.out.println(matcher.groupCount());
|
||||
String shareKey = matcher.group(matcher.groupCount()); // 捕捉组 3
|
||||
System.out.println("Captured part: " + shareKey);
|
||||
} else {
|
||||
System.out.println("No match found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/8/8 2:39
|
||||
*/
|
||||
public class PanDomainTemplateTest {
|
||||
|
||||
@Test
|
||||
public void normalizeShareLink() {
|
||||
// 准备测试数据
|
||||
String testShareUrl = "https://test.lanzoux.com/s/someShareKey";
|
||||
|
||||
PanDomainTemplate template = PanDomainTemplate.fromShareUrl(testShareUrl); // 假设使用LZ网盘模板
|
||||
|
||||
// 调用normalizeShareLink方法
|
||||
ShareLinkInfo result = template.getShareLinkInfo();
|
||||
System.out.println(result);
|
||||
// 断言结果是否符合预期
|
||||
assertNotNull("Result should not be null", result);
|
||||
assertEquals("Share key should match", "someShareKey", result.getShareKey());
|
||||
assertEquals("Standard URL should be generated correctly", template.getStandardUrlTemplate().replace("{shareKey}", "someShareKey"), result.getStandardUrl());
|
||||
// 可以添加更多的断言来验证其他字段
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromShareUrl() throws InterruptedException {
|
||||
// 准备测试数据
|
||||
String lzUrl = "https://wwn.lanzouy.com/ihLkw1gezutg";
|
||||
String cowUrl = "https://cowtransfer.com/s/9a644fe3e3a748";
|
||||
String ceUrl = "https://pan.huang1111.cn/s/g31PcQ";
|
||||
String wsUrl = "https://f.ws59.cn/f/f25625rv6p6";
|
||||
PanDomainTemplate.fromShareUrl(wsUrl).createTool()
|
||||
.parse().onSuccess(System.out::println);
|
||||
PanDomainTemplate.fromShareUrl(lzUrl).createTool()
|
||||
.parse().onSuccess(System.out::println);
|
||||
PanDomainTemplate.fromShareUrl(cowUrl).createTool()
|
||||
.parse().onSuccess(System.out::println);
|
||||
PanDomainTemplate.fromShareUrl(lzUrl).createTool()
|
||||
.parse().onSuccess(System.out::println);
|
||||
|
||||
// PanDomainTemplate.fromShortName("lz").generateShareLink("ihLkw1gezutg")
|
||||
// .createTool().parse().onSuccess(System.out::println);
|
||||
// PanDomainTemplate.LZ.generateShareLink("ihLkw1gezutg")
|
||||
// .createTool().parse().onSuccess(System.out::println);
|
||||
|
||||
|
||||
// 调用fromShareUrl方法
|
||||
// PanDomainTemplate resultTemplate = PanDomainTemplate.fromShareUrl(testShareUrl);
|
||||
// System.out.println(resultTemplate.normalizeShareLink(testShareUrl));
|
||||
// System.out.println(resultTemplate.generateShareLink("xxx"));
|
||||
// System.out.println(resultTemplate.createTool("xxx",null).parse()
|
||||
// .onSuccess(System.out::println));
|
||||
// System.out.println(resultTemplate.getDisplayName());
|
||||
// System.out.println(resultTemplate.getStandardUrlTemplate());
|
||||
// System.out.println(resultTemplate.getRegexPattern());
|
||||
//
|
||||
// // 断言结果是否符合预期
|
||||
// assertNotNull("Result should not be null", resultTemplate);
|
||||
// assertEquals("Should return the correct template", PanDomainTemplate.LZ, resultTemplate);
|
||||
// // 可以添加更多的断言来验证正则表达式匹配逻辑
|
||||
// new Scanner(System.in).nextLine();
|
||||
TimeUnit.SECONDS.sleep(5);
|
||||
}
|
||||
}
|
||||
2
pom.xml
2
pom.xml
@@ -17,7 +17,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<revision>0.1.7</revision>
|
||||
<revision>0.1.8</revision>
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
|
||||
6
web-front/package-lock.json
generated
6
web-front/package-lock.json
generated
@@ -6086,7 +6086,7 @@
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"path-to-regexp": "0.1.8",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.11.0",
|
||||
"range-parser": "~1.2.1",
|
||||
@@ -8746,8 +8746,8 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.8.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
|
||||
"dev": true
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ package cn.qaiu.lz;
|
||||
|
||||
import cn.qaiu.WebClientVertxInit;
|
||||
import cn.qaiu.db.pool.JDBCPoolInit;
|
||||
import cn.qaiu.lz.common.cache.CacheConfigLoader;
|
||||
import cn.qaiu.vx.core.Deploy;
|
||||
import cn.qaiu.vx.core.util.ConfigConstant;
|
||||
import cn.qaiu.vx.core.util.VertxHolder;
|
||||
@@ -23,17 +24,20 @@ public class AppMain {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化数据库
|
||||
* 初始化数据库/缓存等
|
||||
*
|
||||
* @param jsonObject 配置
|
||||
*/
|
||||
private static void exec(JsonObject jsonObject) {
|
||||
WebClientVertxInit.init(VertxHolder.getVertxInstance());
|
||||
DatabindCodec.mapper().registerModule(new JavaTimeModule());
|
||||
// 数据库
|
||||
if (jsonObject.getJsonObject(ConfigConstant.SERVER).getBoolean("enableDatabase")) {
|
||||
JDBCPoolInit.builder().config(jsonObject.getJsonObject("dataSource")).build().initPool();
|
||||
}
|
||||
// 缓存
|
||||
if (jsonObject.containsKey(ConfigConstant.CACHE)) {
|
||||
CacheConfigLoader.init(jsonObject.getJsonObject(ConfigConstant.CACHE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
37
web-service/src/main/java/cn/qaiu/lz/common/cache/CacheConfigLoader.java
vendored
Normal file
37
web-service/src/main/java/cn/qaiu/lz/common/cache/CacheConfigLoader.java
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package cn.qaiu.lz.common.cache;
|
||||
|
||||
import cn.qaiu.parser.PanDomainTemplate;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/12 7:38
|
||||
*/
|
||||
public class CacheConfigLoader {
|
||||
private static final Map<String, Integer> CONFIGS = new HashMap<>();
|
||||
public static String TYPE;
|
||||
public static Integer DEFAULT_DURATION;
|
||||
|
||||
public static void init(JsonObject config) {
|
||||
TYPE = config.getString("type");
|
||||
Integer defaultDuration = config.getInteger("defaultDuration");
|
||||
DEFAULT_DURATION = defaultDuration == null ? 60 : defaultDuration;
|
||||
config.getJsonObject("duration").getMap().forEach((k,v) -> {
|
||||
if (v == null) {
|
||||
CONFIGS.put(k, DEFAULT_DURATION);
|
||||
} else {
|
||||
CONFIGS.put(k, (Integer) v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Integer getDuration(PanDomainTemplate pdt) {
|
||||
return CONFIGS.get(pdt.getShortName());
|
||||
}
|
||||
public static Integer getDuration(String shortName) {
|
||||
return CONFIGS.get(shortName);
|
||||
}
|
||||
}
|
||||
52
web-service/src/main/java/cn/qaiu/lz/common/cache/CacheManager.java
vendored
Normal file
52
web-service/src/main/java/cn/qaiu/lz/common/cache/CacheManager.java
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package cn.qaiu.lz.common.cache;
|
||||
|
||||
import cn.qaiu.db.pool.JDBCPoolInit;
|
||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.jdbcclient.JDBCPool;
|
||||
import io.vertx.sqlclient.templates.SqlTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CacheManager {
|
||||
private final JDBCPool jdbcPool = JDBCPoolInit.instance().getPool();
|
||||
|
||||
|
||||
public Future<CacheLinkInfo> get(String cacheKey) {
|
||||
String sql = "SELECT direct_link as directLink, expiration FROM cache_link_info WHERE share_key = #{share_key}";
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("share_key", cacheKey);
|
||||
Promise<CacheLinkInfo> promise = Promise.promise();
|
||||
SqlTemplate.forQuery(jdbcPool, sql)
|
||||
.mapTo(CacheLinkInfo.class)
|
||||
.execute(params)
|
||||
.onSuccess(rows->{
|
||||
CacheLinkInfo cacheHit;
|
||||
if (rows.size() > 0) {
|
||||
cacheHit = rows.iterator().next();
|
||||
cacheHit.setCacheHit(true);
|
||||
} else {
|
||||
cacheHit = new CacheLinkInfo(JsonObject.of("cacheHit", false));
|
||||
}
|
||||
promise.complete(cacheHit);
|
||||
}).onFailure(Throwable::printStackTrace);
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
|
||||
// 插入或更新缓存数据
|
||||
public Future<Void> cacheShareLink(CacheLinkInfo cacheLinkInfo) {
|
||||
String sql = "MERGE INTO cache_link_info (share_key, direct_link, expiration) " +
|
||||
"KEY (share_key) " +
|
||||
"VALUES (#{shareKey}, #{directLink}, #{expiration})";
|
||||
|
||||
// 直接传递 CacheLinkInfo 实体类
|
||||
return SqlTemplate.forUpdate(jdbcPool, sql)
|
||||
.mapFrom(CacheLinkInfo.class) // 将实体类映射为 Tuple 参数
|
||||
.execute(cacheLinkInfo)
|
||||
.mapEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package cn.qaiu.lz.common.util;
|
||||
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.http.HttpServerRequest;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 处理URL截断问题,拼接被截断的参数,特殊处理pwd参数。
|
||||
*
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/13
|
||||
*/
|
||||
public class URLParamUtil {
|
||||
|
||||
/**
|
||||
* 解析并处理截断的URL参数
|
||||
*
|
||||
* @param request HttpServerRequest对象
|
||||
* @return 完整的URL字符串
|
||||
*/
|
||||
public static String parserParams(HttpServerRequest request) {
|
||||
String url = request.absoluteURI();
|
||||
MultiMap params = request.params();
|
||||
|
||||
// 处理URL截断的情况,例如: url='https://...&key=...&code=...'
|
||||
if (params.contains("url")) {
|
||||
String encodedUrl = params.get("url");
|
||||
url = handleTruncatedUrl(encodedUrl, params);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理被截断的URL,拼接所有参数,特殊处理pwd参数。
|
||||
*
|
||||
* @param encodedUrl 被截断的url参数
|
||||
* @param params 请求的其他参数
|
||||
* @return 重新拼接后的完整URL
|
||||
*/
|
||||
private static String handleTruncatedUrl(String encodedUrl, MultiMap params) {
|
||||
// 对URL进行解码,以便获取完整的URL
|
||||
String decodedUrl = URLDecoder.decode(encodedUrl, StandardCharsets.UTF_8);
|
||||
|
||||
// 如果URL已经包含查询参数,不需要额外拼接
|
||||
if (params.contains("pwd")) {
|
||||
if (params.size() == 2) {
|
||||
return decodedUrl;
|
||||
}
|
||||
} else {
|
||||
if (params.size() == 1) {
|
||||
return decodedUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// 拼接被截断的URL参数,忽略pwd参数
|
||||
StringBuilder urlBuilder = new StringBuilder(decodedUrl);
|
||||
boolean firstParam = !decodedUrl.contains("?");
|
||||
|
||||
for (String paramName : params.names()) {
|
||||
if (!paramName.equals("url") && !paramName.equals("pwd")) { // 忽略 "url" 和 "pwd" 参数
|
||||
if (firstParam) {
|
||||
urlBuilder.append("?");
|
||||
firstParam = false;
|
||||
} else {
|
||||
urlBuilder.append("&");
|
||||
}
|
||||
urlBuilder.append(paramName).append("=").append(params.get(paramName));
|
||||
}
|
||||
}
|
||||
|
||||
return urlBuilder.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package cn.qaiu.lz.web.http;
|
||||
|
||||
import cn.qaiu.parser.IPanTool;
|
||||
import cn.qaiu.parser.impl.EcTool;
|
||||
import cn.qaiu.parser.impl.QQTool;
|
||||
import cn.qaiu.lz.common.util.URLParamUtil;
|
||||
import cn.qaiu.lz.web.service.CacheService;
|
||||
import cn.qaiu.parser.PanDomainTemplate;
|
||||
import cn.qaiu.vx.core.annotaions.RouteHandler;
|
||||
import cn.qaiu.vx.core.annotaions.RouteMapping;
|
||||
import cn.qaiu.vx.core.enums.RouteMethod;
|
||||
import cn.qaiu.vx.core.util.AsyncServiceUtil;
|
||||
import cn.qaiu.vx.core.util.ResponseUtil;
|
||||
import cn.qaiu.vx.core.util.VertxHolder;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.http.HttpServerRequest;
|
||||
@@ -24,33 +24,27 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@RouteHandler("/")
|
||||
public class ServerApi {
|
||||
|
||||
@RouteMapping(value = "/parser", method = RouteMethod.GET, order = 4)
|
||||
public Future<Void> parse(HttpServerResponse response, HttpServerRequest request, String url, String pwd) {
|
||||
private final CacheService cacheService = AsyncServiceUtil.getAsyncServiceInstance(CacheService.class);
|
||||
|
||||
@RouteMapping(value = "/parser", method = RouteMethod.GET, order = 4)
|
||||
public Future<Void> parse(HttpServerResponse response, HttpServerRequest request, String pwd) {
|
||||
Promise<Void> promise = Promise.promise();
|
||||
if (url.contains(EcTool.SHARE_URL_PREFIX)) {
|
||||
// 默认读取Url参数会被截断手动获取一下其他参数
|
||||
url = EcTool.SHARE_URL_PREFIX + request.getParam("data");
|
||||
}
|
||||
if (url.contains(QQTool.SHARE_URL_PREFIX)) {
|
||||
// 默认读取Url参数会被截断手动获取一下其他参数
|
||||
url = url + "&key=" + request.getParam("key") +
|
||||
"&code=" + request.getParam("code") + "&k=" + request.getParam("k") +
|
||||
"&fweb=" + request.getParam("fweb") + "&cl=" + request.getParam("cl");
|
||||
}
|
||||
IPanTool.shareURLPrefixMatching(url, pwd).parse()
|
||||
.onSuccess(resUrl -> ResponseUtil.redirect(response, resUrl, promise))
|
||||
String url = URLParamUtil.parserParams(request);
|
||||
|
||||
PanDomainTemplate panDomainTemplate = PanDomainTemplate.fromShareUrl(url).setShareLinkInfoPwd(pwd);
|
||||
cacheService.getAndSaveCachedShareLink(panDomainTemplate)
|
||||
.onSuccess(res -> ResponseUtil.redirect(
|
||||
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
||||
.putHeader("nfd-cache-expires", res.getExpires()),
|
||||
res.getDirectLink(), promise))
|
||||
.onFailure(t -> promise.fail(t.fillInStackTrace()));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@RouteMapping(value = "/json/parser", method = RouteMethod.GET, order = 3)
|
||||
public Future<String> parseJson(HttpServerRequest request, String url, String pwd) {
|
||||
if (url.contains(EcTool.SHARE_URL_PREFIX)) {
|
||||
// 默认读取Url参数会被截断手动获取一下其他参数
|
||||
url = EcTool.SHARE_URL_PREFIX + request.getParam("data");
|
||||
}
|
||||
return IPanTool.shareURLPrefixMatching(url, pwd).parse();
|
||||
public Future<String> parseJson(HttpServerRequest request, String pwd) {
|
||||
String url = URLParamUtil.parserParams(request);
|
||||
return PanDomainTemplate.fromShareUrl(url).setShareLinkInfoPwd(pwd).createTool().parse();
|
||||
}
|
||||
|
||||
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET, order = 2)
|
||||
@@ -61,7 +55,8 @@ public class ServerApi {
|
||||
key = keys[0];
|
||||
code = keys[1];
|
||||
}
|
||||
return IPanTool.typeMatching(type, key, code).parse();
|
||||
return PanDomainTemplate.fromShortName(type)
|
||||
.generateShareLink(key).setShareLinkInfoPwd(code).createTool().parse();
|
||||
}
|
||||
|
||||
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET, order = 1)
|
||||
@@ -74,8 +69,16 @@ public class ServerApi {
|
||||
code = keys[1];
|
||||
}
|
||||
|
||||
IPanTool.typeMatching(type, key, code).parse()
|
||||
.onSuccess(resUrl -> ResponseUtil.redirect(response, resUrl, promise))
|
||||
PanDomainTemplate panDomainTemplate = PanDomainTemplate
|
||||
.fromShortName(type)
|
||||
.generateShareLink(key)
|
||||
.setShareLinkInfoPwd(code);
|
||||
|
||||
cacheService.getAndSaveCachedShareLink(panDomainTemplate)
|
||||
.onSuccess(res -> ResponseUtil.redirect(
|
||||
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
||||
.putHeader("nfd-cache-expires", res.getExpires()),
|
||||
res.getDirectLink(), promise))
|
||||
.onFailure(t -> promise.fail(t.fillInStackTrace()));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.qaiu.lz.web.model;
|
||||
|
||||
import cn.qaiu.db.ddl.Length;
|
||||
import cn.qaiu.db.ddl.Table;
|
||||
import cn.qaiu.db.ddl.TableGenIgnore;
|
||||
import cn.qaiu.lz.common.ToJson;
|
||||
import io.vertx.codegen.annotations.DataObject;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/11 16:06
|
||||
*/
|
||||
@Table(value = "cache_link_info", keyFields = "share_key")
|
||||
@Data
|
||||
@DataObject
|
||||
@NoArgsConstructor
|
||||
public class CacheLinkInfo implements ToJson {
|
||||
|
||||
/**
|
||||
* 缓存key: type@ShareKey; e.g. lz@xxxx
|
||||
*/
|
||||
@Length(varcharSize = 4096)
|
||||
private String shareKey;
|
||||
|
||||
/**
|
||||
* 解析后的直链
|
||||
*/
|
||||
@Length(varcharSize = 4096)
|
||||
private String directLink;
|
||||
|
||||
/**
|
||||
* 是否命中缓存
|
||||
*/
|
||||
@TableGenIgnore
|
||||
private Boolean cacheHit;
|
||||
|
||||
/**
|
||||
* 到期时间 yyyy-MM-dd hh:mm:ss
|
||||
*/
|
||||
@TableGenIgnore
|
||||
private String expires;
|
||||
|
||||
/**
|
||||
* 有效期
|
||||
*/
|
||||
private Long expiration;
|
||||
|
||||
|
||||
// 使用 JsonObject 构造
|
||||
public CacheLinkInfo(JsonObject json) {
|
||||
CacheLinkInfoConverter.fromJson(json, this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.qaiu.lz.web.model;
|
||||
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
// CacheLinkInfoConverter.java
|
||||
public class CacheLinkInfoConverter {
|
||||
|
||||
public static void fromJson(JsonObject json, CacheLinkInfo obj) {
|
||||
if (json.containsKey("shareKey")) {
|
||||
obj.setShareKey(json.getString("shareKey"));
|
||||
}
|
||||
if (json.containsKey("directLink")) {
|
||||
obj.setDirectLink(json.getString("directLink"));
|
||||
}
|
||||
if (json.containsKey("expires")) {
|
||||
obj.setExpires(json.getString("expires"));
|
||||
}
|
||||
if (json.containsKey("expiration")) {
|
||||
obj.setExpiration(json.getLong("expiration"));
|
||||
}
|
||||
obj.setCacheHit(json.getBoolean("cacheHit", false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.qaiu.lz.web.service;
|
||||
|
||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||
import cn.qaiu.parser.PanDomainTemplate;
|
||||
import cn.qaiu.vx.core.base.BaseAsyncService;
|
||||
import io.vertx.codegen.annotations.ProxyGen;
|
||||
import io.vertx.core.Future;
|
||||
|
||||
/**
|
||||
* @author <a href="https://qaiu.top">QAIU</a>
|
||||
* @date 2024/9/12 8:26
|
||||
*/
|
||||
@ProxyGen
|
||||
public interface CacheService extends BaseAsyncService {
|
||||
|
||||
Future<CacheLinkInfo> getAndSaveCachedShareLink(PanDomainTemplate shareLinkInfo);
|
||||
}
|
||||
@@ -19,4 +19,5 @@ public interface DbService extends BaseAsyncService {
|
||||
Future<JsonObject> sayOk2(String data, UserInfo holder);
|
||||
|
||||
Future<StatisticsInfo> getStatisticsInfo();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package cn.qaiu.lz.web.service;
|
||||
|
||||
import cn.qaiu.vx.core.util.CastUtil;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* JDK代理类工厂
|
||||
*/
|
||||
public class JdkProxyFactory {
|
||||
public static <T> T getProxy(T target) {
|
||||
return CastUtil.cast(Proxy.newProxyInstance(
|
||||
target.getClass().getClassLoader(),
|
||||
target.getClass().getInterfaces(),
|
||||
new ServiceJdkProxy<>(target))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package cn.qaiu.lz.web.service.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.lz.common.cache.CacheConfigLoader;
|
||||
import cn.qaiu.lz.common.cache.CacheManager;
|
||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||
import cn.qaiu.lz.web.service.CacheService;
|
||||
import cn.qaiu.parser.PanDomainTemplate;
|
||||
import cn.qaiu.vx.core.annotaions.Service;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Service
|
||||
public class CacheServiceImpl implements CacheService {
|
||||
|
||||
private final CacheManager cacheManager = new CacheManager();
|
||||
|
||||
@Override
|
||||
public Future<CacheLinkInfo> getAndSaveCachedShareLink(PanDomainTemplate template) {
|
||||
Promise<CacheLinkInfo> promise = Promise.promise();
|
||||
|
||||
// 构建组合的缓存key
|
||||
ShareLinkInfo shareLinkInfo = template.getShareLinkInfo();
|
||||
String cacheKey = generateCacheKey(shareLinkInfo.getType(), shareLinkInfo.getShareKey());
|
||||
// 尝试从缓存中获取
|
||||
cacheManager.get(cacheKey).onSuccess(result -> {
|
||||
// 判断是否已过期
|
||||
// 未命中或者过期
|
||||
if (!result.getCacheHit() || result.getExpiration() < System.currentTimeMillis()) {
|
||||
template.createTool().parse().onSuccess(redirectUrl -> {
|
||||
long expires = System.currentTimeMillis() +
|
||||
CacheConfigLoader.getDuration(shareLinkInfo.getType()) * 60 * 1000;
|
||||
result.setDirectLink(redirectUrl);
|
||||
// result.setExpires(generateDate(expires));
|
||||
promise.complete(result);
|
||||
// 更新缓存
|
||||
// 将直链存储到缓存
|
||||
CacheLinkInfo cacheLinkInfo = new CacheLinkInfo(JsonObject.of(
|
||||
"directLink", redirectUrl,
|
||||
"expiration", expires,
|
||||
"shareKey", cacheKey
|
||||
));
|
||||
cacheManager.cacheShareLink(cacheLinkInfo).onFailure(Throwable::printStackTrace);
|
||||
}).onFailure(promise::fail);
|
||||
} else {
|
||||
result.setExpires(generateDate(result.getExpiration()));
|
||||
promise.complete(result);
|
||||
}
|
||||
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private String generateCacheKey(String type, String shareKey) {
|
||||
// 将type和shareKey组合成一个字符串作为缓存key
|
||||
return type + ":" + shareKey;
|
||||
}
|
||||
|
||||
private String generateDate(Long ts) {
|
||||
return DateFormatUtils.format(new Date(ts), "yyyy-MM-dd hh:mm:ss");
|
||||
}
|
||||
}
|
||||
@@ -39,3 +39,23 @@ dataSource:
|
||||
driverClassName: org.h2.Driver
|
||||
username: root
|
||||
password: '123456'
|
||||
|
||||
# 缓存配置
|
||||
cache:
|
||||
type: h2db
|
||||
# 默认时长: 单位分钟, 实际有效期分钟-1
|
||||
defaultDuration: 59
|
||||
duration:
|
||||
ce:
|
||||
cow:
|
||||
ec:
|
||||
fc:
|
||||
fj:
|
||||
iz:
|
||||
le: 2879
|
||||
lz:
|
||||
qq:
|
||||
ws:
|
||||
ye:
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 要激活的配置: dev--连接本地数据库; prod连接线上数据库
|
||||
active: dev
|
||||
# 版本号
|
||||
version_app: 0.1.7
|
||||
version_app: 0.1.8
|
||||
# 公司名称 -> LOGO版权文字
|
||||
copyright: QAIU
|
||||
|
||||
10
web-service/src/main/resources/http-tools/pan-cx.http
Normal file
10
web-service/src/main/resources/http-tools/pan-cx.http
Normal file
@@ -0,0 +1,10 @@
|
||||
###超星
|
||||
|
||||
### 直链 Referer
|
||||
https://d0.ananas.chaoxing.com/download/8e8c9baca640d24680d974331390a158?at_=1717958244333&ak_=783925f9ba6eb2d0c711977b777a13e0&ad_=58ffecd38be494bea68f0cda68b18c0a&fn=testgles.c
|
||||
Referer: https://pan-yz.chaoxing.com/external/m/file/1006748113111711744
|
||||
|
||||
|
||||
|
||||
###
|
||||
|
||||
@@ -59,3 +59,10 @@ Origin:https://www.feijipan.com
|
||||
Referer:https://www.feijipan.com/
|
||||
|
||||
{"loginName":"","loginPwd":""}
|
||||
|
||||
|
||||
### 240530 规则修改
|
||||
### https://share.feijipan.com/s/nMtCOXL
|
||||
POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&uuid=WQAl5yBy1naGudJEILBvE&extra=2×tamp=A8ECC3C7C50191ACEB9CB8444FD37624&shareId=nMtCOXL&type=0&offset=1&limit=60
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
### 蓝奏云
|
||||
### PASS 蓝奏云
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://lanzoux.com/ia2cntg
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.lanzoux.com/ia2cntg
|
||||
|
||||
### 蓝奏云
|
||||
# @no-redirect
|
||||
@@ -20,7 +20,7 @@ GET http://127.0.0.1:6400/lz/icBp6qqj82b@QAIU
|
||||
### 蓝奏云
|
||||
GET http://127.0.0.1:6400/json/lz/ia2cntg
|
||||
|
||||
### 蓝奏云优享
|
||||
### PASS 蓝奏云优享
|
||||
GET http://127.0.0.1:6400/json/iz/lGFndCM
|
||||
|
||||
###
|
||||
@@ -34,7 +34,7 @@ GET http://127.0.0.1:6400/json/parser?url=https://www.ilanzou.com/s/zHkna1S
|
||||
GET http://127.0.0.1:6400/json/cow/9a644fe3e3a748
|
||||
Referer: https://cowtransfer.com/
|
||||
|
||||
### 奶牛
|
||||
### PASS 奶牛
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/cow/e4f41b51b5da4f
|
||||
|
||||
@@ -47,9 +47,9 @@ GET http://127.0.0.1:6400/parser?url=https://cowtransfer.com/s/9a644fe3e3a748
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://goldrepo.cowtransfer.com/s/026a638795634b
|
||||
|
||||
### 移动云空间 https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||
### 移动云空间 PASS 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
|
||||
@@ -62,21 +62,7 @@ GET http://127.0.0.1:6400/json/ec/4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx@T6O
|
||||
#@no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1&pwd=T6O1RC
|
||||
|
||||
|
||||
|
||||
### UC网盘
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/uc/33197dd53ace4
|
||||
|
||||
### UC网盘
|
||||
GET http://127.0.0.1:6400/json/uc/33197dd53ace4
|
||||
|
||||
### UC网盘
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://fast.uc.cn/s/33197dd53ace4
|
||||
|
||||
|
||||
### 小飞机盘https://share.feijipan.com/s/nMtCOXL
|
||||
### 小飞机盘 PASS https://share.feijipan.com/s/nMtCOXL
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://share.feijipan.com/s/nMtCOXL
|
||||
|
||||
@@ -98,7 +84,7 @@ GET http://127.0.0.1:6400/parser?url=https://v2.fangcloud.com/sharing/fb54bdf03c
|
||||
### 360亿方云
|
||||
GET http://127.0.0.1:6400/json/fc/fb54bdf03c66c04334fe3687a3
|
||||
|
||||
### 360亿方云
|
||||
### 360亿方云 PASS
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/fc/e5079007dc31226096628870c7@QAIU
|
||||
|
||||
@@ -113,7 +99,7 @@ GET http://127.0.0.1:6400/json/ye/iaKtVv-6OECd@DcGe
|
||||
### 123
|
||||
GET https://lz.qaiu.top/json/ye/iaKtVv-6OECd@DcGe
|
||||
|
||||
### 123
|
||||
### 123 PASS
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/ye/iaKtVv-qOECd
|
||||
|
||||
@@ -121,7 +107,7 @@ GET http://127.0.0.1:6400/ye/iaKtVv-qOECd
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/ye/Ev1lVv-t3SY3
|
||||
|
||||
### 123 https://www.123pan.com/s/iaKtVv-6OECd.html
|
||||
### 123 PASS https://www.123pan.com/s/iaKtVv-6OECd.html
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/json/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.html&pwd=DcGe
|
||||
|
||||
@@ -131,29 +117,37 @@ GET http://127.0.0.1:6400/json/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.
|
||||
GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/zF07Vv-WkHWd.html&pwd=bios
|
||||
|
||||
|
||||
### 联想乐云
|
||||
### 联想乐云 PASS
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://lecloud.lenovo.com/share/4DANWdRQsHHyiFB4a
|
||||
|
||||
### 联想乐云
|
||||
### 联想乐云 PASS
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/le/4DANWdRQsHHyiFB4a
|
||||
|
||||
### 联想乐云
|
||||
### 联想乐云 PASS
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/le/2RkKbLP9BrppS9b43@ex2b
|
||||
|
||||
### 联想乐云
|
||||
### 联想乐云 PASS
|
||||
GET http://127.0.0.1:6400/json/le/2RkKbLP9BrppS9b43@ex2b
|
||||
|
||||
### 文叔叔
|
||||
GET http://127.0.0.1:6400/json/parser?url=https://f.ws59.cn/f/e3peohu6192
|
||||
### PASS 文叔叔
|
||||
GET http://127.0.0.1:6400/json/parser?url=https://f.ws59.cn/f/f25625rv6p6
|
||||
###
|
||||
https://f.wss.cc/f/f25625rv6p6
|
||||
|
||||
### Cloudreve
|
||||
### TODO Cloudreve
|
||||
GET http://127.0.0.1:6400/json/ce/https_pan.huang1111.cn_s_wDz5TK
|
||||
|
||||
### Cloudreve https://pan.huang1111.cn/s/g31PcQ
|
||||
GET http://127.0.0.1:6400/json/parser?url=https://pan.huang1111.cn/s/g31PcQ&pwd=qaiu
|
||||
GET http://127.0.0.1:6400/parser?url=https://pan.huang1111.cn/s/g31PcQ&pwd=qaiu
|
||||
|
||||
### PASS QQ
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6400/parser?url=https://iwx.mail.qq.com/ftn/download?func=3&key=c79c5732038ad41cf5ef1e3261663537f2cf4133636635371d4c484657050c0e5d561d070252021a0a0552024e5257530b4e0157540256525e5751565a053b374014540057560c070b511e53130d21f0361c829bf15b3e4a6355fbada6dd10dfdd11a33263663537386330&code=8c02cf57&k=c79c5732038ad41cf5ef1e3261663537f2cf4133636635371d4c484657050c0e5d561d070252021a0a0552024e5257530b4e0157540256525e5751565a053b374014540057560c070b511e53130d21f0361c829bf15b3e4a6355fbada6dd10dfdd11a33263663537386330
|
||||
|
||||
|
||||
|
||||
###
|
||||
GET http://127.0.0.1:6400/v2/statisticsInfo
|
||||
|
||||
Reference in New Issue
Block a user