mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 12:23:03 +00:00
1. 缓存优化
This commit is contained in:
@@ -203,9 +203,9 @@ bash service-install.sh
|
|||||||
|
|
||||||
## 0.1.8 开发计划
|
## 0.1.8 开发计划
|
||||||
- Docker部署
|
- Docker部署
|
||||||
- 联想乐云解析
|
- 联想乐云解析 √
|
||||||
- 直链缓存
|
- 直链缓存 √
|
||||||
- 日志优化
|
- 日志优化 √
|
||||||
|
|
||||||
|
|
||||||
**技术栈:**
|
**技术栈:**
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class ShareLinkInfo {
|
|||||||
// 建造者类
|
// 建造者类
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private String shareKey; // 分享键
|
private String shareKey; // 分享键
|
||||||
private String type; // 分享类型
|
private String type; // 分享类型 (网盘模板枚举的小写)
|
||||||
private String sharePassword = ""; // 分享密码(如果存在)
|
private String sharePassword = ""; // 分享密码(如果存在)
|
||||||
private String shareUrl; // 原始分享链接
|
private String shareUrl; // 原始分享链接
|
||||||
private String standardUrl; // 规范化的标准链接
|
private String standardUrl; // 规范化的标准链接
|
||||||
|
|||||||
@@ -4,28 +4,4 @@ import io.vertx.core.Future;
|
|||||||
|
|
||||||
public interface IPanTool {
|
public interface IPanTool {
|
||||||
Future<String> parse();
|
Future<String> parse();
|
||||||
// 基于枚举PanDomainTemplate匹配分享链接类型
|
|
||||||
static IPanTool typeMatching(String type, String key, String pwd) {
|
|
||||||
try {
|
|
||||||
return PanDomainTemplate
|
|
||||||
.fromShortName(type)
|
|
||||||
.generateShareLink(key)
|
|
||||||
.setShareLinkInfoPwd(pwd)
|
|
||||||
.createTool();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new UnsupportedOperationException("未知分享类型", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static IPanTool shareURLPrefixMatching(String url, String pwd) {
|
|
||||||
try {
|
|
||||||
return PanDomainTemplate
|
|
||||||
.fromShareUrl(url)
|
|
||||||
.setShareLinkInfoPwd(pwd)
|
|
||||||
.createTool();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new UnsupportedOperationException("未知分享类型", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,15 @@
|
|||||||
package cn.qaiu.parser;
|
package cn.qaiu.parser;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.impl.*;
|
import cn.qaiu.parser.impl.*;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 枚举类 PanDomainTemplate 定义了不同网盘服务的模板信息,包括:
|
* 枚举类 PanDomainTemplate 定义了不同网盘服务的模板信息,包括:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>displayName: 网盘服务的显示名称,用于用户界面展示。</li>
|
* <li>displayName: 网盘服务的显示名称,用于用户界面展示。</li>
|
||||||
* <li>shortName: 网盘服务的简称,用于内部逻辑处理,例如API路径。</li>
|
|
||||||
* <li>regexPattern: 用于匹配和解析分享链接的正则表达式。</li>
|
* <li>regexPattern: 用于匹配和解析分享链接的正则表达式。</li>
|
||||||
* <li>standardUrlTemplate: 网盘服务的标准URL模板,用于规范化分享链接。</li>
|
* <li>standardUrlTemplate: 网盘服务的标准URL模板,用于规范化分享链接。</li>
|
||||||
* <li>toolClass: 网盘解析工具实现类。</li>
|
* <li>toolClass: 网盘解析工具实现类。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* 该类提供方法来解析和规范化不同来源的分享链接,确保它们可以转换为统一的标准链接格式。
|
|
||||||
* 通过这种方式,应用程序可以更容易地处理和识别不同网盘服务的分享链接.
|
|
||||||
*
|
*
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
* @author <a href="https://qaiu.top">QAIU</a>
|
||||||
* at 2023/6/13 4:26
|
* at 2023/6/13 4:26
|
||||||
@@ -26,7 +18,6 @@ public enum PanDomainTemplate {
|
|||||||
|
|
||||||
// 网盘定义
|
// 网盘定义
|
||||||
LZ("蓝奏云",
|
LZ("蓝奏云",
|
||||||
"lz",
|
|
||||||
"https://([a-z]+)?\\.?lanzou[a-z]\\.com/(.+/)?(.+)",
|
"https://([a-z]+)?\\.?lanzou[a-z]\\.com/(.+/)?(.+)",
|
||||||
"https://lanzoux.com/{shareKey}",
|
"https://lanzoux.com/{shareKey}",
|
||||||
LzTool.class),
|
LzTool.class),
|
||||||
@@ -34,64 +25,54 @@ public enum PanDomainTemplate {
|
|||||||
// https://www.feijix.com/s/
|
// https://www.feijix.com/s/
|
||||||
// https://share.feijipan.com/s/
|
// https://share.feijipan.com/s/
|
||||||
FJ("小飞机网盘",
|
FJ("小飞机网盘",
|
||||||
"fj",
|
|
||||||
"https://(share\\.feijipan\\.com|www\\.feijix\\.com)/s/(.+)",
|
"https://(share\\.feijipan\\.com|www\\.feijix\\.com)/s/(.+)",
|
||||||
"https://www.feijix.com/s/{shareKey}",
|
"https://www.feijix.com/s/{shareKey}",
|
||||||
FjTool.class),
|
FjTool.class),
|
||||||
|
|
||||||
// https://lecloud.lenovo.com/share/
|
// https://lecloud.lenovo.com/share/
|
||||||
LE("联想乐云",
|
LE("联想乐云",
|
||||||
"le",
|
|
||||||
"https://lecloud?\\.lenovo\\.com/share/(.+)",
|
"https://lecloud?\\.lenovo\\.com/share/(.+)",
|
||||||
"https://lecloud.lenovo.com/share/{shareKey}",
|
"https://lecloud.lenovo.com/share/{shareKey}",
|
||||||
LeTool.class),
|
LeTool.class),
|
||||||
|
|
||||||
// https://v2.fangcloud.com/s/
|
// https://v2.fangcloud.com/s/
|
||||||
FC("亿方云",
|
FC("亿方云",
|
||||||
"fc",
|
|
||||||
"https://v2\\.fangcloud\\.(com|cn)/(s|sharing)/([^/]+)",
|
"https://v2\\.fangcloud\\.(com|cn)/(s|sharing)/([^/]+)",
|
||||||
"https://v2.fangcloud.com/s/{shareKey}",
|
"https://v2.fangcloud.com/s/{shareKey}",
|
||||||
FcTool.class),
|
FcTool.class),
|
||||||
// https://www.ilanzou.com/s/
|
// https://www.ilanzou.com/s/
|
||||||
IZ("蓝奏云优享",
|
IZ("蓝奏云优享",
|
||||||
"iz",
|
|
||||||
"https://www\\.ilanzou\\.com/s/(.+)",
|
"https://www\\.ilanzou\\.com/s/(.+)",
|
||||||
"https://www.ilanzou.com/s/{shareKey}",
|
"https://www.ilanzou.com/s/{shareKey}",
|
||||||
IzTool.class),
|
IzTool.class),
|
||||||
// https://wx.mail.qq.com/ftn/download?
|
// https://wx.mail.qq.com/ftn/download?
|
||||||
QQ("QQ邮箱中转站",
|
QQ("QQ邮箱中转站",
|
||||||
"qq",
|
|
||||||
"https://i?wx\\.mail\\.qq\\.com/ftn/download\\?(.+)",
|
"https://i?wx\\.mail\\.qq\\.com/ftn/download\\?(.+)",
|
||||||
"https://iwx.mail.qq.com/ftn/download/{shareKey}",
|
"https://iwx.mail.qq.com/ftn/download/{shareKey}",
|
||||||
QQTool.class),
|
QQTool.class),
|
||||||
// https://f.ws59.cn/f/或者https://www.wenshushu.cn/f/
|
// https://f.ws59.cn/f/或者https://www.wenshushu.cn/f/
|
||||||
WS("文叔叔",
|
WS("文叔叔",
|
||||||
"ws",
|
|
||||||
"https://(f\\.ws59\\.cn|www\\.wenshushu\\.cn)/f/(.+)",
|
"https://(f\\.ws59\\.cn|www\\.wenshushu\\.cn)/f/(.+)",
|
||||||
"https://f.ws59.cn/f/{shareKey}",
|
"https://f.ws59.cn/f/{shareKey}",
|
||||||
WsTool.class),
|
WsTool.class),
|
||||||
// https://www.123pan.com/s/
|
// https://www.123pan.com/s/
|
||||||
YE("123网盘",
|
YE("123网盘",
|
||||||
"ye",
|
|
||||||
"https://www\\.123pan\\.com/s/(.+)\\.html",
|
"https://www\\.123pan\\.com/s/(.+)\\.html",
|
||||||
"https://www.123pan.com/s/{shareKey}.html",
|
"https://www.123pan.com/s/{shareKey}.html",
|
||||||
YeTool.class),
|
YeTool.class),
|
||||||
// https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={code}&isShare=1
|
// https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={code}&isShare=1
|
||||||
EC("移动云空间",
|
EC("移动云空间",
|
||||||
"ec",
|
|
||||||
"https://www\\.ecpan\\.cn/web(/%23|/#)?/yunpanProxy\\?path=.*&data=" +
|
"https://www\\.ecpan\\.cn/web(/%23|/#)?/yunpanProxy\\?path=.*&data=" +
|
||||||
"([^&]+)&isShare=1",
|
"([^&]+)&isShare=1",
|
||||||
"https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={shareKey}&isShare=1",
|
"https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={shareKey}&isShare=1",
|
||||||
EcTool.class),
|
EcTool.class),
|
||||||
// https://cowtransfer.com/s/
|
// https://cowtransfer.com/s/
|
||||||
COW("奶牛快传",
|
COW("奶牛快传",
|
||||||
"cow",
|
|
||||||
"https://(.*)cowtransfer\\.com/s/(.+)",
|
"https://(.*)cowtransfer\\.com/s/(.+)",
|
||||||
"https://cowtransfer.com/s/{shareKey}",
|
"https://cowtransfer.com/s/{shareKey}",
|
||||||
CowTool.class),
|
CowTool.class),
|
||||||
// https://pan.huang1111.cn/s/
|
// https://pan.huang1111.cn/s/
|
||||||
CE("huang1111",
|
CE("huang1111",
|
||||||
"ce",
|
|
||||||
"https://pan\\.huang1111\\.cn/s/(.+)",
|
"https://pan\\.huang1111\\.cn/s/(.+)",
|
||||||
"https://pan.huang1111.cn/s/{shareKey}",
|
"https://pan.huang1111.cn/s/{shareKey}",
|
||||||
CeTool.class);
|
CeTool.class);
|
||||||
@@ -100,79 +81,25 @@ public enum PanDomainTemplate {
|
|||||||
// 网盘的显示名称,用于用户界面显示
|
// 网盘的显示名称,用于用户界面显示
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
|
|
||||||
// 网盘的简短名称,用于内部逻辑处理,如REST API路径
|
|
||||||
private final String shortName;
|
|
||||||
|
|
||||||
// 用于匹配和解析分享链接的正则表达式
|
// 用于匹配和解析分享链接的正则表达式
|
||||||
private final String regexPattern;
|
private final String regexPattern;
|
||||||
|
|
||||||
// 网盘的标准链接模板,不含占位符,用于规范化分享链接
|
// 网盘的标准链接模板,不含占位符,用于规范化分享链接
|
||||||
private final String standardUrlTemplate;
|
private final String standardUrlTemplate;
|
||||||
private final ShareLinkInfo shareLinkInfo;
|
|
||||||
// 指向IPanTool实现类
|
// 指向解析工具IPanTool实现类
|
||||||
private final Class<? extends IPanTool> toolClass;
|
private final Class<? extends IPanTool> toolClass;
|
||||||
|
|
||||||
PanDomainTemplate(String displayName, String shortName, String regexPattern,
|
PanDomainTemplate(String displayName, String regexPattern, String standardUrlTemplate,
|
||||||
String standardUrlTemplate, Class<? extends IPanTool> toolClass) {
|
Class<? extends IPanTool> toolClass) {
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
this.shortName = shortName;
|
|
||||||
this.regexPattern = regexPattern;
|
this.regexPattern = regexPattern;
|
||||||
this.standardUrlTemplate = standardUrlTemplate;
|
this.standardUrlTemplate = standardUrlTemplate;
|
||||||
this.toolClass = toolClass;
|
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() {
|
public String getDisplayName() {
|
||||||
return this.displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRegexPattern() {
|
public String getRegexPattern() {
|
||||||
@@ -183,43 +110,7 @@ public enum PanDomainTemplate {
|
|||||||
return standardUrlTemplate;
|
return standardUrlTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Class<? extends IPanTool> getToolClass() {
|
||||||
public ShareLinkInfo getShareLinkInfo() {
|
return toolClass;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
113
parser/src/main/java/cn/qaiu/parser/ParserCreate.java
Normal file
113
parser/src/main/java/cn/qaiu/parser/ParserCreate.java
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package cn.qaiu.parser;
|
||||||
|
|
||||||
|
import cn.qaiu.entity.ShareLinkInfo;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该类提供方法来解析和规范化不同来源的分享链接,确保它们可以转换为统一的标准链接格式。
|
||||||
|
* 通过这种方式,应用程序可以更容易地处理和识别不同网盘服务的分享链接。
|
||||||
|
*
|
||||||
|
* @author <a href="https://qaiu.top">QAIU</a>
|
||||||
|
* @date 2024/9/15 14:10
|
||||||
|
*/
|
||||||
|
public class ParserCreate {
|
||||||
|
private final PanDomainTemplate panDomainTemplate;
|
||||||
|
private final ShareLinkInfo shareLinkInfo;
|
||||||
|
|
||||||
|
public ParserCreate(PanDomainTemplate panDomainTemplate, ShareLinkInfo shareLinkInfo) {
|
||||||
|
this.panDomainTemplate = panDomainTemplate;
|
||||||
|
this.shareLinkInfo = shareLinkInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 解析并规范化分享链接
|
||||||
|
public ParserCreate 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(this.panDomainTemplate.getRegexPattern());
|
||||||
|
Matcher matcher = pattern.matcher(shareUrl);
|
||||||
|
if (matcher.find()) {
|
||||||
|
String shareKey = matcher.group(matcher.groupCount());
|
||||||
|
// 返回规范化的标准链接
|
||||||
|
String standardUrl = getStandardUrlTemplate().replace("{shareKey}", shareKey);
|
||||||
|
shareLinkInfo.setShareUrl(shareUrl);
|
||||||
|
shareLinkInfo.setShareKey(shareKey);
|
||||||
|
shareLinkInfo.setStandardUrl(standardUrl);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid share URL for " + this.panDomainTemplate.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
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 this.panDomainTemplate.getToolClass()
|
||||||
|
.getDeclaredConstructor(ShareLinkInfo.class)
|
||||||
|
.newInstance(shareLinkInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("无法创建工具实例: " + panDomainTemplate.getToolClass().getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set share key
|
||||||
|
public ParserCreate shareKey(String shareKey) {
|
||||||
|
shareLinkInfo.setShareKey(shareKey);
|
||||||
|
shareLinkInfo.setStandardUrl(panDomainTemplate.getStandardUrlTemplate().replace("{shareKey}", shareKey));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStandardUrlTemplate() {
|
||||||
|
return this.panDomainTemplate.getStandardUrlTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShareLinkInfo getShareLinkInfo() {
|
||||||
|
return shareLinkInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParserCreate setShareLinkInfoPwd(String pwd) {
|
||||||
|
shareLinkInfo.setSharePassword(pwd);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据分享链接获取PanDomainTemplate实例
|
||||||
|
public synchronized static ParserCreate fromShareUrl(String shareUrl) {
|
||||||
|
for (PanDomainTemplate panDomainTemplate : PanDomainTemplate.values()) {
|
||||||
|
if (shareUrl.matches(panDomainTemplate.getRegexPattern())) {
|
||||||
|
ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder()
|
||||||
|
.type(panDomainTemplate.name().toLowerCase())
|
||||||
|
.shareUrl(shareUrl).build();
|
||||||
|
ParserCreate parserCreate = new ParserCreate(panDomainTemplate, shareLinkInfo);
|
||||||
|
return parserCreate.normalizeShareLink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unsupported share URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据type获取枚举实例
|
||||||
|
public synchronized static ParserCreate fromType(String type) {
|
||||||
|
try {
|
||||||
|
PanDomainTemplate panDomainTemplate = Enum.valueOf(PanDomainTemplate.class, type.toUpperCase());
|
||||||
|
ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder()
|
||||||
|
.type(type.toLowerCase()).build();
|
||||||
|
return new ParserCreate(panDomainTemplate, shareLinkInfo);
|
||||||
|
} catch (IllegalArgumentException ignore) {
|
||||||
|
// 如果没有找到对应的枚举实例,抛出异常
|
||||||
|
throw new IllegalArgumentException("No enum constant for type name: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,15 +19,15 @@ public class PanDomainTemplateTest {
|
|||||||
// 准备测试数据
|
// 准备测试数据
|
||||||
String testShareUrl = "https://test.lanzoux.com/s/someShareKey";
|
String testShareUrl = "https://test.lanzoux.com/s/someShareKey";
|
||||||
|
|
||||||
PanDomainTemplate template = PanDomainTemplate.fromShareUrl(testShareUrl); // 假设使用LZ网盘模板
|
ParserCreate parserCreate = ParserCreate.fromShareUrl(testShareUrl); // 假设使用LZ网盘模板
|
||||||
|
|
||||||
// 调用normalizeShareLink方法
|
// 调用normalizeShareLink方法
|
||||||
ShareLinkInfo result = template.getShareLinkInfo();
|
ShareLinkInfo result = parserCreate.getShareLinkInfo();
|
||||||
System.out.println(result);
|
System.out.println(result);
|
||||||
// 断言结果是否符合预期
|
// 断言结果是否符合预期
|
||||||
assertNotNull("Result should not be null", result);
|
assertNotNull("Result should not be null", result);
|
||||||
assertEquals("Share key should match", "someShareKey", result.getShareKey());
|
assertEquals("Share key should match", "someShareKey", result.getShareKey());
|
||||||
assertEquals("Standard URL should be generated correctly", template.getStandardUrlTemplate().replace("{shareKey}", "someShareKey"), result.getStandardUrl());
|
assertEquals("Standard URL should be generated correctly", parserCreate.getStandardUrlTemplate().replace("{shareKey}", "someShareKey"), result.getStandardUrl());
|
||||||
// 可以添加更多的断言来验证其他字段
|
// 可以添加更多的断言来验证其他字段
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,25 +38,25 @@ public class PanDomainTemplateTest {
|
|||||||
String cowUrl = "https://cowtransfer.com/s/9a644fe3e3a748";
|
String cowUrl = "https://cowtransfer.com/s/9a644fe3e3a748";
|
||||||
String ceUrl = "https://pan.huang1111.cn/s/g31PcQ";
|
String ceUrl = "https://pan.huang1111.cn/s/g31PcQ";
|
||||||
String wsUrl = "https://f.ws59.cn/f/f25625rv6p6";
|
String wsUrl = "https://f.ws59.cn/f/f25625rv6p6";
|
||||||
PanDomainTemplate.fromShareUrl(wsUrl).createTool()
|
// ParserCreate.fromShareUrl(wsUrl).createTool()
|
||||||
.parse().onSuccess(System.out::println);
|
// .parse().onSuccess(System.out::println);
|
||||||
PanDomainTemplate.fromShareUrl(lzUrl).createTool()
|
// ParserCreate.fromShareUrl(lzUrl).createTool()
|
||||||
.parse().onSuccess(System.out::println);
|
// .parse().onSuccess(System.out::println);
|
||||||
PanDomainTemplate.fromShareUrl(cowUrl).createTool()
|
// ParserCreate.fromShareUrl(cowUrl).createTool()
|
||||||
.parse().onSuccess(System.out::println);
|
// .parse().onSuccess(System.out::println);
|
||||||
PanDomainTemplate.fromShareUrl(lzUrl).createTool()
|
ParserCreate.fromShareUrl(lzUrl).createTool()
|
||||||
.parse().onSuccess(System.out::println);
|
.parse().onSuccess(System.out::println);
|
||||||
|
|
||||||
// PanDomainTemplate.fromShortName("lz").generateShareLink("ihLkw1gezutg")
|
// ParserCreate.fromType("lz").shareKey("ihLkw1gezutg")
|
||||||
// .createTool().parse().onSuccess(System.out::println);
|
// .createTool().parse().onSuccess(System.out::println);
|
||||||
// PanDomainTemplate.LZ.generateShareLink("ihLkw1gezutg")
|
// ParserCreate.LZ.shareKey("ihLkw1gezutg")
|
||||||
// .createTool().parse().onSuccess(System.out::println);
|
// .createTool().parse().onSuccess(System.out::println);
|
||||||
|
|
||||||
|
|
||||||
// 调用fromShareUrl方法
|
// 调用fromShareUrl方法
|
||||||
// PanDomainTemplate resultTemplate = PanDomainTemplate.fromShareUrl(testShareUrl);
|
// PanDomainTemplate resultTemplate = ParserCreate.fromShareUrl(testShareUrl);
|
||||||
// System.out.println(resultTemplate.normalizeShareLink(testShareUrl));
|
// System.out.println(resultTemplate.normalizeShareLink(testShareUrl));
|
||||||
// System.out.println(resultTemplate.generateShareLink("xxx"));
|
// System.out.println(resultTemplate.shareKey("xxx"));
|
||||||
// System.out.println(resultTemplate.createTool("xxx",null).parse()
|
// System.out.println(resultTemplate.createTool("xxx",null).parse()
|
||||||
// .onSuccess(System.out::println));
|
// .onSuccess(System.out::println));
|
||||||
// System.out.println(resultTemplate.getDisplayName());
|
// System.out.println(resultTemplate.getDisplayName());
|
||||||
@@ -65,7 +65,7 @@ public class PanDomainTemplateTest {
|
|||||||
//
|
//
|
||||||
// // 断言结果是否符合预期
|
// // 断言结果是否符合预期
|
||||||
// assertNotNull("Result should not be null", resultTemplate);
|
// assertNotNull("Result should not be null", resultTemplate);
|
||||||
// assertEquals("Should return the correct template", PanDomainTemplate.LZ, resultTemplate);
|
// assertEquals("Should return the correct template", ParserCreate.LZ, resultTemplate);
|
||||||
// // 可以添加更多的断言来验证正则表达式匹配逻辑
|
// // 可以添加更多的断言来验证正则表达式匹配逻辑
|
||||||
// new Scanner(System.in).nextLine();
|
// new Scanner(System.in).nextLine();
|
||||||
TimeUnit.SECONDS.sleep(5);
|
TimeUnit.SECONDS.sleep(5);
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
content="Netdisk fast download 网盘直链解析工具">
|
content="Netdisk fast download 网盘直链解析工具">
|
||||||
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
|
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
|
||||||
<script>LA.init({id:"K8zkCkZMgFA6ShZK",ck:"K8zkCkZMgFA6ShZK"})</script>
|
<script>LA.init({id:"K8zkCkZMgFA6ShZK",ck:"K8zkCkZMgFA6ShZK"})</script>
|
||||||
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9851170484804006"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
<style>
|
<style>
|
||||||
.page-loading-wrap {
|
.page-loading-wrap {
|
||||||
padding: 120px;
|
padding: 120px;
|
||||||
|
|||||||
@@ -11,15 +11,12 @@
|
|||||||
<div class="typo">
|
<div class="typo">
|
||||||
<p><strong>项目GitHub </strong><a href="https://github.com/qaiu/netdisk-fast-download" target="_blank"
|
<p><strong>项目GitHub </strong><a href="https://github.com/qaiu/netdisk-fast-download" target="_blank"
|
||||||
rel="nofollow"><u>netdisk-fast-download</u></a></p>
|
rel="nofollow"><u>netdisk-fast-download</u></a></p>
|
||||||
<p><strong>当前页面修改自开源项目</strong><a href="https://github.com/HurryBy/CloudDiskAnalysis"
|
<p><strong>目前支持 </strong>蓝奏云/蓝奏云优享/小飞机盘/123云盘/奶牛快传/移动云云空间/亿方云/文叔叔/QQ邮箱文件中转站</p>
|
||||||
target="_blank"
|
|
||||||
rel="nofollow"><u>CloudDiskAnalysis</u></a></p>
|
|
||||||
<p><strong>目前支持 </strong>已支持蓝奏云/奶牛快传/移动云云空间/UC网盘(暂时失效)/小飞机盘/亿方云/123云盘</p>
|
|
||||||
<p>
|
<p>
|
||||||
<el-button><strong @click="getInfo">刷新API调用统计</strong></el-button>
|
<el-button><strong @click="getInfo">刷新API调用统计</strong></el-button>
|
||||||
</p>
|
</p>
|
||||||
<p>节点1: 成功:{{ node1Info.success }},失败:{{ node1Info.fail }},总数:{{ node1Info.total }}</p>
|
<p>节点1: 回源请求数:{{ node1Info.parserTotal }}, 缓存请求数:{{ node1Info.cacheTotal }}, 总数:{{ node1Info.total }}</p>
|
||||||
<p>节点2: 成功:{{ node2Info.success }},失败:{{ node2Info.fail }},总数:{{ node2Info.total }}</p>
|
<!-- <p>节点2: 成功:{{ node2Info.success }},失败:{{ node2Info.fail }},总数:{{ node2Info.total }}</p>-->
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="main" v-loading="isLoading">
|
<div class="main" v-loading="isLoading">
|
||||||
@@ -153,6 +150,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$message.error(response.data.msg)
|
this.$message.error(response.data.msg)
|
||||||
}
|
}
|
||||||
|
this.getInfo()
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
@@ -174,12 +172,12 @@ export default {
|
|||||||
this.node1Info = response.data.data
|
this.node1Info = response.data.data
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
axios.get('/n2/statisticsInfo').then(
|
// axios.get('/n2/statisticsInfo').then(
|
||||||
response => {
|
// response => {
|
||||||
if (response.data.success) {
|
// if (response.data.success) {
|
||||||
this.node2Info = response.data.data
|
// this.node2Info = response.data.data
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ public class CacheConfigLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Integer getDuration(PanDomainTemplate pdt) {
|
public static Integer getDuration(PanDomainTemplate pdt) {
|
||||||
return CONFIGS.get(pdt.getShortName());
|
return CONFIGS.get(pdt.name().toLowerCase());
|
||||||
}
|
}
|
||||||
public static Integer getDuration(String shortName) {
|
public static Integer getDuration(String type) {
|
||||||
return CONFIGS.get(shortName);
|
return CONFIGS.get(type.toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import io.vertx.core.Future;
|
|||||||
import io.vertx.core.Promise;
|
import io.vertx.core.Promise;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
import io.vertx.jdbcclient.JDBCPool;
|
import io.vertx.jdbcclient.JDBCPool;
|
||||||
|
import io.vertx.sqlclient.Row;
|
||||||
import io.vertx.sqlclient.templates.SqlTemplate;
|
import io.vertx.sqlclient.templates.SqlTemplate;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -16,7 +17,7 @@ public class CacheManager {
|
|||||||
|
|
||||||
|
|
||||||
public Future<CacheLinkInfo> get(String cacheKey) {
|
public Future<CacheLinkInfo> get(String cacheKey) {
|
||||||
String sql = "SELECT direct_link as directLink, expiration FROM cache_link_info WHERE share_key = #{share_key}";
|
String sql = "SELECT share_key as shareKey, direct_link as directLink, expiration FROM cache_link_info WHERE share_key = #{share_key}";
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.put("share_key", cacheKey);
|
params.put("share_key", cacheKey);
|
||||||
Promise<CacheLinkInfo> promise = Promise.promise();
|
Promise<CacheLinkInfo> promise = Promise.promise();
|
||||||
@@ -29,7 +30,7 @@ public class CacheManager {
|
|||||||
cacheHit = rows.iterator().next();
|
cacheHit = rows.iterator().next();
|
||||||
cacheHit.setCacheHit(true);
|
cacheHit.setCacheHit(true);
|
||||||
} else {
|
} else {
|
||||||
cacheHit = new CacheLinkInfo(JsonObject.of("cacheHit", false));
|
cacheHit = new CacheLinkInfo(JsonObject.of("cacheHit", false, "shareKey", cacheKey));
|
||||||
}
|
}
|
||||||
promise.complete(cacheHit);
|
promise.complete(cacheHit);
|
||||||
}).onFailure(Throwable::printStackTrace);
|
}).onFailure(Throwable::printStackTrace);
|
||||||
@@ -49,4 +50,80 @@ public class CacheManager {
|
|||||||
.execute(cacheLinkInfo)
|
.execute(cacheLinkInfo)
|
||||||
.mapEmpty();
|
.mapEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 统计网盘厂商API解析次数
|
||||||
|
public Future<Integer> updateTotalByCached(String shareKey) {
|
||||||
|
Promise<Integer> promise = Promise.promise();
|
||||||
|
String sql = """
|
||||||
|
MERGE INTO `api_statistics_info` (`pan_type`, `share_key`, `cache_hit_total`, `update_ts`)
|
||||||
|
KEY (`share_key`)
|
||||||
|
VALUES (#{panType}, #{shareKey}, #{total}, #{ts})
|
||||||
|
""";
|
||||||
|
|
||||||
|
getShareKeyTotal(shareKey, "cache_hit_total").onSuccess(total -> {
|
||||||
|
Integer newTotal = (total == null ? 0 : total) + 1;
|
||||||
|
SqlTemplate.forUpdate(jdbcPool, sql)
|
||||||
|
.execute(new HashMap<>() {{
|
||||||
|
put("panType", getShareType(shareKey));
|
||||||
|
put("shareKey", shareKey);
|
||||||
|
put("total", newTotal);
|
||||||
|
put("ts", System.currentTimeMillis());
|
||||||
|
}})
|
||||||
|
.onSuccess(res -> promise.complete(res.rowCount()))
|
||||||
|
.onFailure(Throwable::printStackTrace);
|
||||||
|
});
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getShareType(String fullShareKey) {
|
||||||
|
// 将type和shareKey组合成一个字符串作为缓存key
|
||||||
|
return fullShareKey.split(":")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统计网盘厂商API解析次数
|
||||||
|
public Future<Integer> updateTotalByParser(String shareKey) {
|
||||||
|
Promise<Integer> promise = Promise.promise();
|
||||||
|
String sql = """
|
||||||
|
MERGE INTO `api_statistics_info` (`pan_type`, `share_key`, `api_parser_total`, `update_ts`)
|
||||||
|
KEY (`share_key`)
|
||||||
|
VALUES (#{panType}, #{shareKey}, #{total}, #{ts})
|
||||||
|
""";
|
||||||
|
|
||||||
|
getShareKeyTotal(shareKey, "api_parser_total").onSuccess(total -> {
|
||||||
|
Integer newTotal = (total == null ? 0 : total) + 1;
|
||||||
|
SqlTemplate.forUpdate(jdbcPool, sql)
|
||||||
|
.execute(new HashMap<>() {{
|
||||||
|
put("panType", getShareType(shareKey));
|
||||||
|
put("shareKey", shareKey);
|
||||||
|
put("total", newTotal);
|
||||||
|
put("ts", System.currentTimeMillis());
|
||||||
|
}})
|
||||||
|
.onSuccess(res -> promise.complete(res.rowCount()))
|
||||||
|
.onFailure(Throwable::printStackTrace);
|
||||||
|
});
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Future<Integer> getShareKeyTotal(String shareKey, String name) {
|
||||||
|
String sql = """
|
||||||
|
select `share_key`, sum({total_name}) sum_num
|
||||||
|
from `api_statistics_info`
|
||||||
|
group by `share_key` having `share_key` = #{shareKey};
|
||||||
|
""".replace("{total_name}", name);
|
||||||
|
Promise<Integer> promise = Promise.promise();
|
||||||
|
Map<String, Object> paramMap = new HashMap<>();
|
||||||
|
paramMap.put("shareKey", shareKey);
|
||||||
|
SqlTemplate.forQuery(jdbcPool, sql)
|
||||||
|
.mapTo(Row::toJson)
|
||||||
|
.execute(paramMap)
|
||||||
|
.onSuccess(res -> {
|
||||||
|
Integer total = res.iterator().hasNext() ?
|
||||||
|
res.iterator().next().getInteger("sum_num") : null;
|
||||||
|
promise.complete(total);
|
||||||
|
});
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class DefaultInterceptor implements BeforeInterceptor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(RoutingContext ctx) {
|
public void handle(RoutingContext ctx) {
|
||||||
System.out.println("进入前置拦截器1->" + ctx.request().path());
|
// System.out.println("进入前置拦截器1->" + ctx.request().path());
|
||||||
doNext(ctx);
|
doNext(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import cn.qaiu.db.pool.JDBCPoolInit;
|
|||||||
import cn.qaiu.lz.common.model.ParserLogInfo;
|
import cn.qaiu.lz.common.model.ParserLogInfo;
|
||||||
import cn.qaiu.vx.core.annotaions.HandleSortFilter;
|
import cn.qaiu.vx.core.annotaions.HandleSortFilter;
|
||||||
import cn.qaiu.vx.core.interceptor.AfterInterceptor;
|
import cn.qaiu.vx.core.interceptor.AfterInterceptor;
|
||||||
import cn.qaiu.vx.core.model.JsonResult;
|
|
||||||
import cn.qaiu.vx.core.util.CommonUtil;
|
import cn.qaiu.vx.core.util.CommonUtil;
|
||||||
import cn.qaiu.vx.core.util.SharedDataUtil;
|
import cn.qaiu.vx.core.util.SharedDataUtil;
|
||||||
import io.vertx.core.json.JsonArray;
|
import io.vertx.core.json.JsonArray;
|
||||||
@@ -36,21 +35,8 @@ public class LogStatistics implements AfterInterceptor {
|
|||||||
|
|
||||||
ParserLogInfo parserLogInfo = new ParserLogInfo();
|
ParserLogInfo parserLogInfo = new ParserLogInfo();
|
||||||
parserLogInfo.setPath(ctx.request().uri());
|
parserLogInfo.setPath(ctx.request().uri());
|
||||||
if (responseData == null) {
|
if (responseData.containsKey("code") && responseData.getInteger("code") == 500) {
|
||||||
String location = ctx.response().headers().get("location");
|
log.error("code 500: {} {}", ctx.request().path(), responseData.getString("msg"));
|
||||||
if (location != null) {
|
|
||||||
parserLogInfo.setCode(200);
|
|
||||||
parserLogInfo.setData(location);
|
|
||||||
} else {
|
|
||||||
log.error("location不存在且responseData为空, path={}", ctx.request().path());
|
|
||||||
}
|
|
||||||
insert(parserLogInfo);
|
|
||||||
|
|
||||||
} else if (responseData.containsKey("code")) {
|
|
||||||
JsonResult<?> result = JsonResult.toJsonResult(responseData);
|
|
||||||
parserLogInfo.setCode(result.getCode());
|
|
||||||
parserLogInfo.setData(result.getCode() == 500 ? result.getMsg() : result.getData().toString());
|
|
||||||
insert(parserLogInfo);
|
|
||||||
} else {
|
} else {
|
||||||
log.error("未知json日志: {}, path: {}", responseData.encode(), ctx.request().path());
|
log.error("未知json日志: {}, path: {}", responseData.encode(), ctx.request().path());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ public class ParserLogInfo {
|
|||||||
String id = SnowflakeIdWorker.getStringId();
|
String id = SnowflakeIdWorker.getStringId();
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS", timezone = "GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS", timezone = "GMT+8")
|
||||||
Date logTime = new Date();
|
Date logTime = new Date();
|
||||||
|
|
||||||
|
@Length(varcharSize = 4096)
|
||||||
String path;
|
String path;
|
||||||
Integer code;
|
Integer code;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package cn.qaiu.lz.web.http;
|
package cn.qaiu.lz.web.http;
|
||||||
|
|
||||||
import cn.qaiu.lz.common.util.URLParamUtil;
|
import cn.qaiu.lz.common.util.URLParamUtil;
|
||||||
|
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||||
import cn.qaiu.lz.web.service.CacheService;
|
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.RouteHandler;
|
||||||
import cn.qaiu.vx.core.annotaions.RouteMapping;
|
import cn.qaiu.vx.core.annotaions.RouteMapping;
|
||||||
import cn.qaiu.vx.core.enums.RouteMethod;
|
import cn.qaiu.vx.core.enums.RouteMethod;
|
||||||
@@ -31,8 +31,7 @@ public class ServerApi {
|
|||||||
Promise<Void> promise = Promise.promise();
|
Promise<Void> promise = Promise.promise();
|
||||||
String url = URLParamUtil.parserParams(request);
|
String url = URLParamUtil.parserParams(request);
|
||||||
|
|
||||||
PanDomainTemplate panDomainTemplate = PanDomainTemplate.fromShareUrl(url).setShareLinkInfoPwd(pwd);
|
cacheService.getCachedByShareUrlAndPwd(url, pwd)
|
||||||
cacheService.getAndSaveCachedShareLink(panDomainTemplate)
|
|
||||||
.onSuccess(res -> ResponseUtil.redirect(
|
.onSuccess(res -> ResponseUtil.redirect(
|
||||||
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
||||||
.putHeader("nfd-cache-expires", res.getExpires()),
|
.putHeader("nfd-cache-expires", res.getExpires()),
|
||||||
@@ -42,39 +41,32 @@ public class ServerApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RouteMapping(value = "/json/parser", method = RouteMethod.GET, order = 3)
|
@RouteMapping(value = "/json/parser", method = RouteMethod.GET, order = 3)
|
||||||
public Future<String> parseJson(HttpServerRequest request, String pwd) {
|
public Future<CacheLinkInfo> parseJson(HttpServerRequest request, String pwd) {
|
||||||
String url = URLParamUtil.parserParams(request);
|
String url = URLParamUtil.parserParams(request);
|
||||||
return PanDomainTemplate.fromShareUrl(url).setShareLinkInfoPwd(pwd).createTool().parse();
|
return cacheService.getCachedByShareUrlAndPwd(url, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET, order = 2)
|
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET, order = 2)
|
||||||
public Future<String> parseKeyJson(String type, String key) {
|
public Future<CacheLinkInfo> parseKeyJson(String type, String key) {
|
||||||
String code = "";
|
String pwd = "";
|
||||||
if (key.contains("@")) {
|
if (key.contains("@")) {
|
||||||
String[] keys = key.split("@");
|
String[] keys = key.split("@");
|
||||||
key = keys[0];
|
key = keys[0];
|
||||||
code = keys[1];
|
pwd = keys[1];
|
||||||
}
|
}
|
||||||
return PanDomainTemplate.fromShortName(type)
|
return cacheService.getCachedByShareKeyAndPwd(type, key, pwd);
|
||||||
.generateShareLink(key).setShareLinkInfoPwd(code).createTool().parse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET, order = 1)
|
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET, order = 1)
|
||||||
public Future<Void> parseKey(HttpServerResponse response, String type, String key) {
|
public Future<Void> parseKey(HttpServerResponse response, String type, String key) {
|
||||||
Promise<Void> promise = Promise.promise();
|
Promise<Void> promise = Promise.promise();
|
||||||
String code = "";
|
String pwd = "";
|
||||||
if (key.contains("@")) {
|
if (key.contains("@")) {
|
||||||
String[] keys = key.split("@");
|
String[] keys = key.split("@");
|
||||||
key = keys[0];
|
key = keys[0];
|
||||||
code = keys[1];
|
pwd = keys[1];
|
||||||
}
|
}
|
||||||
|
cacheService.getCachedByShareKeyAndPwd(type, key, pwd)
|
||||||
PanDomainTemplate panDomainTemplate = PanDomainTemplate
|
|
||||||
.fromShortName(type)
|
|
||||||
.generateShareLink(key)
|
|
||||||
.setShareLinkInfoPwd(code);
|
|
||||||
|
|
||||||
cacheService.getAndSaveCachedShareLink(panDomainTemplate)
|
|
||||||
.onSuccess(res -> ResponseUtil.redirect(
|
.onSuccess(res -> ResponseUtil.redirect(
|
||||||
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
||||||
.putHeader("nfd-cache-expires", res.getExpires()),
|
.putHeader("nfd-cache-expires", res.getExpires()),
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package cn.qaiu.lz.web.model;
|
||||||
|
|
||||||
|
import cn.qaiu.db.ddl.Length;
|
||||||
|
import cn.qaiu.db.ddl.Table;
|
||||||
|
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 = "api_statistics_info", keyFields = "share_key")
|
||||||
|
@Data
|
||||||
|
@DataObject
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ApiStatisticsInfo implements ToJson {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pan type 单独拿出来便于统计.
|
||||||
|
*/
|
||||||
|
@Length(varcharSize = 4)
|
||||||
|
private String panType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享key type:key
|
||||||
|
*/
|
||||||
|
@Length(varcharSize = 4096)
|
||||||
|
private String shareKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命中缓存次数
|
||||||
|
*/
|
||||||
|
private Integer cacheHitTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* api解析次数
|
||||||
|
*/
|
||||||
|
private Integer apiParserTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间戳
|
||||||
|
*/
|
||||||
|
private Long updateTs;
|
||||||
|
|
||||||
|
// 使用 JsonObject 构造
|
||||||
|
public ApiStatisticsInfo(JsonObject json) {
|
||||||
|
if (json.containsKey("panType")) {
|
||||||
|
this.setPanType(json.getString("panType"));
|
||||||
|
}
|
||||||
|
if (json.containsKey("shareKey")) {
|
||||||
|
this.setShareKey(json.getString("shareKey"));
|
||||||
|
}
|
||||||
|
if (json.containsKey("cacheHitTotal")) {
|
||||||
|
this.setCacheHitTotal(json.getInteger("cacheHitTotal"));
|
||||||
|
}
|
||||||
|
if (json.containsKey("apiParserTotal")) {
|
||||||
|
this.setApiParserTotal(json.getInteger("apiParserTotal"));
|
||||||
|
}
|
||||||
|
if (json.containsKey("updateTs")) {
|
||||||
|
this.setUpdateTs(json.getLong("updateTs"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ import lombok.NoArgsConstructor;
|
|||||||
public class CacheLinkInfo implements ToJson {
|
public class CacheLinkInfo implements ToJson {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存key: type@ShareKey; e.g. lz@xxxx
|
* 缓存key: type:ShareKey; e.g. lz:xxxx
|
||||||
*/
|
*/
|
||||||
@Length(varcharSize = 4096)
|
@Length(varcharSize = 4096)
|
||||||
private String shareKey;
|
private String shareKey;
|
||||||
@@ -51,6 +51,20 @@ public class CacheLinkInfo implements ToJson {
|
|||||||
|
|
||||||
// 使用 JsonObject 构造
|
// 使用 JsonObject 构造
|
||||||
public CacheLinkInfo(JsonObject json) {
|
public CacheLinkInfo(JsonObject json) {
|
||||||
CacheLinkInfoConverter.fromJson(json, this);
|
if (json.containsKey("shareKey")) {
|
||||||
|
this.setShareKey(json.getString("shareKey"));
|
||||||
|
}
|
||||||
|
if (json.containsKey("directLink")) {
|
||||||
|
this.setDirectLink(json.getString("directLink"));
|
||||||
|
}
|
||||||
|
if (json.containsKey("expires")) {
|
||||||
|
this.setExpires(json.getString("expires"));
|
||||||
|
}
|
||||||
|
if (json.containsKey("expiration")) {
|
||||||
|
this.setExpiration(json.getLong("expiration"));
|
||||||
|
}
|
||||||
|
this.setCacheHit(json.getBoolean("cacheHit", false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,14 +10,14 @@ import lombok.NoArgsConstructor;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@DataObject
|
@DataObject
|
||||||
public class StatisticsInfo implements ToJson {
|
public class StatisticsInfo implements ToJson {
|
||||||
Integer fail;
|
Integer parserTotal;
|
||||||
Integer success;
|
Integer cacheTotal;
|
||||||
Integer total;
|
Integer total;
|
||||||
|
|
||||||
|
|
||||||
public StatisticsInfo(JsonObject jsonObject) {
|
public StatisticsInfo(JsonObject jsonObject) {
|
||||||
this.fail = jsonObject.getInteger("fail");
|
this.parserTotal = jsonObject.getInteger("parserTotal");
|
||||||
this.success = jsonObject.getInteger("success");
|
this.cacheTotal = jsonObject.getInteger("cacheTotal");
|
||||||
this.total = jsonObject.getInteger("total");
|
this.total = jsonObject.getInteger("total");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package cn.qaiu.lz.web.service;
|
package cn.qaiu.lz.web.service;
|
||||||
|
|
||||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||||
import cn.qaiu.parser.PanDomainTemplate;
|
|
||||||
import cn.qaiu.vx.core.base.BaseAsyncService;
|
import cn.qaiu.vx.core.base.BaseAsyncService;
|
||||||
import io.vertx.codegen.annotations.ProxyGen;
|
import io.vertx.codegen.annotations.ProxyGen;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
@@ -13,5 +12,7 @@ import io.vertx.core.Future;
|
|||||||
@ProxyGen
|
@ProxyGen
|
||||||
public interface CacheService extends BaseAsyncService {
|
public interface CacheService extends BaseAsyncService {
|
||||||
|
|
||||||
Future<CacheLinkInfo> getAndSaveCachedShareLink(PanDomainTemplate shareLinkInfo);
|
Future<CacheLinkInfo> getCachedByShareKeyAndPwd(String type, String shareKey, String pwd);
|
||||||
|
|
||||||
|
Future<CacheLinkInfo> getCachedByShareUrlAndPwd(String shareUrl, String pwd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import cn.qaiu.lz.common.cache.CacheConfigLoader;
|
|||||||
import cn.qaiu.lz.common.cache.CacheManager;
|
import cn.qaiu.lz.common.cache.CacheManager;
|
||||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
||||||
import cn.qaiu.lz.web.service.CacheService;
|
import cn.qaiu.lz.web.service.CacheService;
|
||||||
import cn.qaiu.parser.PanDomainTemplate;
|
import cn.qaiu.parser.ParserCreate;
|
||||||
import cn.qaiu.vx.core.annotaions.Service;
|
import cn.qaiu.vx.core.annotaions.Service;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.Promise;
|
import io.vertx.core.Promise;
|
||||||
@@ -19,21 +19,22 @@ public class CacheServiceImpl implements CacheService {
|
|||||||
|
|
||||||
private final CacheManager cacheManager = new CacheManager();
|
private final CacheManager cacheManager = new CacheManager();
|
||||||
|
|
||||||
@Override
|
private Future<CacheLinkInfo> getAndSaveCachedShareLink(ParserCreate parserCreate) {
|
||||||
public Future<CacheLinkInfo> getAndSaveCachedShareLink(PanDomainTemplate template) {
|
|
||||||
Promise<CacheLinkInfo> promise = Promise.promise();
|
Promise<CacheLinkInfo> promise = Promise.promise();
|
||||||
|
|
||||||
// 构建组合的缓存key
|
// 构建组合的缓存key
|
||||||
ShareLinkInfo shareLinkInfo = template.getShareLinkInfo();
|
ShareLinkInfo shareLinkInfo = parserCreate.getShareLinkInfo();
|
||||||
String cacheKey = generateCacheKey(shareLinkInfo.getType(), shareLinkInfo.getShareKey());
|
String cacheKey = generateCacheKey(shareLinkInfo.getType(), shareLinkInfo.getShareKey());
|
||||||
// 尝试从缓存中获取
|
// 尝试从缓存中获取
|
||||||
cacheManager.get(cacheKey).onSuccess(result -> {
|
cacheManager.get(cacheKey).onSuccess(result -> {
|
||||||
// 判断是否已过期
|
// 判断是否已过期
|
||||||
// 未命中或者过期
|
// 未命中或者过期
|
||||||
if (!result.getCacheHit() || result.getExpiration() < System.currentTimeMillis()) {
|
if (!result.getCacheHit() || result.getExpiration() < System.currentTimeMillis()) {
|
||||||
template.createTool().parse().onSuccess(redirectUrl -> {
|
// parse
|
||||||
|
result.setCacheHit(false);
|
||||||
|
result.setExpiration(0L);
|
||||||
|
parserCreate.createTool().parse().onSuccess(redirectUrl -> {
|
||||||
long expires = System.currentTimeMillis() +
|
long expires = System.currentTimeMillis() +
|
||||||
CacheConfigLoader.getDuration(shareLinkInfo.getType()) * 60 * 1000;
|
CacheConfigLoader.getDuration(shareLinkInfo.getType()) * 60 * 1000L;
|
||||||
result.setDirectLink(redirectUrl);
|
result.setDirectLink(redirectUrl);
|
||||||
// result.setExpires(generateDate(expires));
|
// result.setExpires(generateDate(expires));
|
||||||
promise.complete(result);
|
promise.complete(result);
|
||||||
@@ -45,10 +46,12 @@ public class CacheServiceImpl implements CacheService {
|
|||||||
"shareKey", cacheKey
|
"shareKey", cacheKey
|
||||||
));
|
));
|
||||||
cacheManager.cacheShareLink(cacheLinkInfo).onFailure(Throwable::printStackTrace);
|
cacheManager.cacheShareLink(cacheLinkInfo).onFailure(Throwable::printStackTrace);
|
||||||
|
cacheManager.updateTotalByParser(cacheKey).onFailure(Throwable::printStackTrace);
|
||||||
}).onFailure(promise::fail);
|
}).onFailure(promise::fail);
|
||||||
} else {
|
} else {
|
||||||
result.setExpires(generateDate(result.getExpiration()));
|
result.setExpires(generateDate(result.getExpiration()));
|
||||||
promise.complete(result);
|
promise.complete(result);
|
||||||
|
cacheManager.updateTotalByCached(cacheKey).onFailure(Throwable::printStackTrace);
|
||||||
}
|
}
|
||||||
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
|
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
|
||||||
return promise.future();
|
return promise.future();
|
||||||
@@ -62,4 +65,16 @@ public class CacheServiceImpl implements CacheService {
|
|||||||
private String generateDate(Long ts) {
|
private String generateDate(Long ts) {
|
||||||
return DateFormatUtils.format(new Date(ts), "yyyy-MM-dd hh:mm:ss");
|
return DateFormatUtils.format(new Date(ts), "yyyy-MM-dd hh:mm:ss");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Future<CacheLinkInfo> getCachedByShareKeyAndPwd(String type, String shareKey, String pwd) {
|
||||||
|
ParserCreate parserCreate = ParserCreate.fromType(type).shareKey(shareKey).setShareLinkInfoPwd(pwd);
|
||||||
|
return getAndSaveCachedShareLink(parserCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Future<CacheLinkInfo> getCachedByShareUrlAndPwd(String shareUrl, String pwd) {
|
||||||
|
ParserCreate parserCreate = ParserCreate.fromShareUrl(shareUrl).setShareLinkInfoPwd(pwd);
|
||||||
|
return getAndSaveCachedShareLink(parserCreate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,10 +48,9 @@ public class DbServiceImpl implements DbService {
|
|||||||
JDBCPool client = JDBCPoolInit.instance().getPool();
|
JDBCPool client = JDBCPoolInit.instance().getPool();
|
||||||
Promise<StatisticsInfo> promise = Promise.promise();
|
Promise<StatisticsInfo> promise = Promise.promise();
|
||||||
String sql = """
|
String sql = """
|
||||||
select COUNT(CASE "code" WHEN 500 THEN "code" END ) "fail",
|
select sum(api_parser_total) parserTotal,sum("cache_hit_total") cacheTotal,
|
||||||
COUNT(CASE "code" WHEN 200 THEN "code" END ) "success",
|
sum(api_parser_total) + sum("cache_hit_total") total
|
||||||
count(1) "total"
|
from "api_statistics_info";
|
||||||
from "t_parser_log_info"
|
|
||||||
""";
|
""";
|
||||||
SqlTemplate.forQuery(client, sql).mapTo(StatisticsInfo.class).execute(new HashMap<>()).onSuccess(row -> {
|
SqlTemplate.forQuery(client, sql).mapTo(StatisticsInfo.class).execute(new HashMap<>()).onSuccess(row -> {
|
||||||
StatisticsInfo info;
|
StatisticsInfo info;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ cache:
|
|||||||
iz:
|
iz:
|
||||||
le: 2879
|
le: 2879
|
||||||
lz:
|
lz:
|
||||||
qq:
|
qq: 999999
|
||||||
ws:
|
ws:
|
||||||
ye:
|
ye:
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -8,14 +8,14 @@ GET http://127.0.0.1:6400/json/parser?url=https://wwsd.lanzoue.com/iLany1e9bbbi
|
|||||||
|
|
||||||
### 蓝奏云
|
### 蓝奏云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/lz/i6SqHmp1yfc
|
GET http://127.0.0.1:6400/json/lz/i6SqHmp1yfc
|
||||||
### 蓝奏云https://acgtools.lanzoui.com/iUr7Qnu3sxc https://wwn.lanzouy.com/tp/ihLkw1gezutg https://wwn.lanzouy.com/ihLkw1gezutg
|
### 蓝奏云https://acgtools.lanzoui.com/iUr7Qnu3sxc https://wwn.lanzouy.com/tp/ihLkw1gezutg https://wwn.lanzouy.com/ihLkw1gezutg
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/lz/ihLkw1gezutg
|
GET http://127.0.0.1:6400/lz/ihLkw1gezutg
|
||||||
|
|
||||||
### 蓝奏云
|
### 蓝奏云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/lz/icBp6qqj82b@QAIU
|
GET http://127.0.0.1:6400/json/lz/icBp6qqj82b@QAIU
|
||||||
|
|
||||||
### 蓝奏云
|
### 蓝奏云
|
||||||
GET http://127.0.0.1:6400/json/lz/ia2cntg
|
GET http://127.0.0.1:6400/json/lz/ia2cntg
|
||||||
@@ -133,7 +133,7 @@ GET http://127.0.0.1:6400/le/2RkKbLP9BrppS9b43@ex2b
|
|||||||
GET http://127.0.0.1:6400/json/le/2RkKbLP9BrppS9b43@ex2b
|
GET http://127.0.0.1:6400/json/le/2RkKbLP9BrppS9b43@ex2b
|
||||||
|
|
||||||
### PASS 文叔叔
|
### PASS 文叔叔
|
||||||
GET http://127.0.0.1:6400/json/parser?url=https://f.ws59.cn/f/f25625rv6p6
|
GET http://127.0.0.1:6400/parser?url=https://f.ws59.cn/f/f25625rv6p6
|
||||||
###
|
###
|
||||||
https://f.wss.cc/f/f25625rv6p6
|
https://f.wss.cc/f/f25625rv6p6
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ GET http://127.0.0.1:6400/parser?url=https://pan.huang1111.cn/s/g31PcQ&pwd=qaiu
|
|||||||
|
|
||||||
### PASS QQ
|
### PASS QQ
|
||||||
# @no-redirect
|
# @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/json/parser?url=https://iwx.mail.qq.com/ftn/download?func=3&key=qweqe&code=8c02cf57&k=asdad
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user