diff --git a/core/src/main/java/cn/qaiu/vx/core/handlerfactory/RouterHandlerFactory.java b/core/src/main/java/cn/qaiu/vx/core/handlerfactory/RouterHandlerFactory.java index d7c1b23..fd7d320 100644 --- a/core/src/main/java/cn/qaiu/vx/core/handlerfactory/RouterHandlerFactory.java +++ b/core/src/main/java/cn/qaiu/vx/core/handlerfactory/RouterHandlerFactory.java @@ -175,8 +175,13 @@ public class RouterHandlerFactory implements BaseHttpApi { route.handler(ResponseTimeHandler.create()); route.handler(ctx -> handlerMethod(instance, method, ctx)).failureHandler(ctx -> { if (ctx.response().ended()) return; - ctx.failure().printStackTrace(); - doFireJsonResultResponse(ctx, JsonResult.error(ctx.failure().getMessage(), 500)); + // 超时处理器状态码503 + if (ctx.statusCode() == 503 || ctx.failure() == null) { + doFireJsonResultResponse(ctx, JsonResult.error("未知异常, 请联系管理员", 500)); + } else { + ctx.failure().printStackTrace(); + doFireJsonResultResponse(ctx, JsonResult.error(ctx.failure().getMessage(), 500)); + } }); } else if (method.isAnnotationPresent(SockRouteMapper.class)) { // websocket 基于sockJs diff --git a/core/src/main/java/cn/qaiu/vx/core/util/ResponseUtil.java b/core/src/main/java/cn/qaiu/vx/core/util/ResponseUtil.java index 1fa68e4..4551ab5 100644 --- a/core/src/main/java/cn/qaiu/vx/core/util/ResponseUtil.java +++ b/core/src/main/java/cn/qaiu/vx/core/util/ResponseUtil.java @@ -26,10 +26,20 @@ public class ResponseUtil { .end(jsonObject.encode()); } + public static void fireJsonObjectResponse(HttpServerResponse ctx, JsonObject jsonObject) { + ctx.putHeader(CONTENT_TYPE, "application/json; charset=utf-8") + .setStatusCode(200) + .end(jsonObject.encode()); + } + public static void fireJsonResultResponse(RoutingContext ctx, JsonResult jsonResult) { fireJsonObjectResponse(ctx, jsonResult.toJsonObject()); } + public static void fireJsonResultResponse(HttpServerResponse ctx, JsonResult jsonResult) { + fireJsonObjectResponse(ctx, jsonResult.toJsonObject()); + } + public static void fireTextResponse(RoutingContext ctx, String text) { ctx.response().putHeader(CONTENT_TYPE, "text/html; charset=utf-8").end(text); } diff --git a/parser/src/main/java/cn/qaiu/entity/FileInfo.java b/parser/src/main/java/cn/qaiu/entity/FileInfo.java index 40ce7f7..33a7bf5 100644 --- a/parser/src/main/java/cn/qaiu/entity/FileInfo.java +++ b/parser/src/main/java/cn/qaiu/entity/FileInfo.java @@ -20,9 +20,9 @@ public class FileInfo { Long size; /** - * MIME类型 + * 类型 */ - String fileMIME; + String fileType; /** * 文件路径 @@ -34,6 +34,11 @@ public class FileInfo { */ String createTime; + /** + * 上次修改时间 + */ + String updateTime; + /** * 创建者 */ @@ -81,12 +86,12 @@ public class FileInfo { return this; } - public String getFileMIME() { - return fileMIME; + public String getFileType() { + return fileType; } - public FileInfo setFileMIME(String fileMIME) { - this.fileMIME = fileMIME; + public FileInfo setFileType(String fileType) { + this.fileType = fileType; return this; } @@ -108,6 +113,15 @@ public class FileInfo { return this; } + public String getUpdateTime() { + return updateTime; + } + + public FileInfo setUpdateTime(String updateTime) { + this.updateTime = updateTime; + return this; + } + public String getCreateBy() { return createBy; } diff --git a/parser/src/main/java/cn/qaiu/entity/ShareLinkInfo.java b/parser/src/main/java/cn/qaiu/entity/ShareLinkInfo.java index 371db00..62e0ea5 100644 --- a/parser/src/main/java/cn/qaiu/entity/ShareLinkInfo.java +++ b/parser/src/main/java/cn/qaiu/entity/ShareLinkInfo.java @@ -13,7 +13,13 @@ public class ShareLinkInfo { private String shareUrl; // 原始分享链接 private String standardUrl; // 规范化的标准链接 - private Map otherParam; // 其他参数 + /** + * 其他参数预定义 + * auths: 认证相关 传入 + * UA: 浏览器请求头 传入 + * fileInfo: 解析成功的文件信息对象 传出 + */ + private Map otherParam; private ShareLinkInfo(Builder builder) { this.shareKey = builder.shareKey; diff --git a/parser/src/main/java/cn/qaiu/parser/PanBase.java b/parser/src/main/java/cn/qaiu/parser/PanBase.java index 59bf910..d14d8b5 100644 --- a/parser/src/main/java/cn/qaiu/parser/PanBase.java +++ b/parser/src/main/java/cn/qaiu/parser/PanBase.java @@ -152,6 +152,7 @@ public abstract class PanBase implements IPanTool { return handleFail(""); } + /** * bodyAsJsonObject的封装, 会自动处理异常 * @@ -175,33 +176,6 @@ public abstract class PanBase implements IPanTool { } } - /** - * 解压gzip数据 - * @param compressedData compressedData - * @return String - * @throws IOException IOException - */ - private String decompressGzip(Buffer compressedData) throws IOException { - try (ByteArrayInputStream bais = new ByteArrayInputStream(compressedData.getBytes()); - GZIPInputStream gzis = new GZIPInputStream(bais); - BufferedReader reader = new BufferedReader(new InputStreamReader(gzis, - StandardCharsets.UTF_8))) { - - // 用于存储解压后的字符串 - StringBuilder decompressedData = new StringBuilder(); - - // 逐行读取解压后的数据 - String line; - while ((line = reader.readLine()) != null) { - decompressedData.append(line); - } - - // 此时decompressedData.toString()包含了解压后的字符串 - return decompressedData.toString(); - } - - } - protected void complete(String url) { promise.complete(url); } @@ -232,4 +206,31 @@ public abstract class PanBase implements IPanTool { } } + + /** + * 解压gzip数据 + * @param compressedData compressedData + * @return String + * @throws IOException IOException + */ + private String decompressGzip(Buffer compressedData) throws IOException { + try (ByteArrayInputStream bais = new ByteArrayInputStream(compressedData.getBytes()); + GZIPInputStream gzis = new GZIPInputStream(bais); + BufferedReader reader = new BufferedReader(new InputStreamReader(gzis, + StandardCharsets.UTF_8))) { + + // 用于存储解压后的字符串 + StringBuilder decompressedData = new StringBuilder(); + + // 逐行读取解压后的数据 + String line; + while ((line = reader.readLine()) != null) { + decompressedData.append(line); + } + + // 此时decompressedData.toString()包含了解压后的字符串 + return decompressedData.toString(); + } + + } } diff --git a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java index 76ecece..9262887 100644 --- a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java +++ b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java @@ -59,6 +59,12 @@ public enum PanDomainTemplate { "https://iwx.mail.qq.com/ftn/download/{shareKey}", "https://mail.qq.com", QQTool.class), + // https://wx.mail.qq.com/s?k=uAG9JR42Rqgt010mFp + QQW("QQ邮箱中转站(微信账户)", + compile("https://wx\\.mail\\.qq\\.com/s\\?k=(?.+)"), + "https://wx.mail.qq.com/s?k={shareKey}", + "https://mail.qq.com", + QQwTool.class), // https://f.ws59.cn/f/或者https://www.wenshushu.cn/f/ WS("文叔叔", compile("https://(f\\.ws(\\d{2})\\.cn|www\\.wenshushu\\.cn)/f/(?.+)"), @@ -96,6 +102,7 @@ public enum PanDomainTemplate { "https://www.vyuyun.com/s/{shareKey}?password={pwd}", PvyyTool.class), // https://1drv.ms/w/s!Alg0feQmCv2rnRFd60DQOmMa-Oh_?e=buaRtp + // https://1drv.ms/u/c/abfd0a26e47d3458/EdYACWvPq85Et797YmvL5LgBruUKoNxqIFATXhIv1PI2_Q?e=z4ffNJ POD("OneDrive", compile("https://1drv\\.ms/(?.+)"), "https://1drv\\.ms/{shareKey}", @@ -117,7 +124,7 @@ public enum PanDomainTemplate { "https://www.dropbox.com/scl/fi/{shareKey}/?rlkey={pwd}&dl=0", PdbTool.class), P115("115网盘", - compile("https://(115|anxia).com/s/(?\\w+)(\\?password=(?\\w+))?(&.+)?"), + compile("https://(115|anxia).com/s/(?\\w+)(\\?password=(?\\w+))?([&#].*)?"), "https://115.com/s/{shareKey}?password={pwd}", P115Tool.class), // =====================音乐类解析 分享链接标志->MxxS (单歌曲/普通音质)========================== diff --git a/parser/src/main/java/cn/qaiu/parser/impl/QQwTool.java b/parser/src/main/java/cn/qaiu/parser/impl/QQwTool.java new file mode 100644 index 0000000..a457868 --- /dev/null +++ b/parser/src/main/java/cn/qaiu/parser/impl/QQwTool.java @@ -0,0 +1,56 @@ +package cn.qaiu.parser.impl; + +import cn.qaiu.entity.ShareLinkInfo; +import io.vertx.core.Future; +import io.vertx.core.MultiMap; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class QQwTool extends QQTool { + + public QQwTool(ShareLinkInfo shareLinkInfo) { + super(shareLinkInfo); + } + + @Override + public Future parse() { + client.getAbs(shareLinkInfo.getStandardUrl()).send().onSuccess(res -> { + String html = res.bodyAsString(); + String url = extractVariables(html).get("url"); + if (url != null) { + String url302 = url.replace("\\x26", "&"); + clientNoRedirects.getAbs(url302).send().onSuccess(res2 -> { + MultiMap headers = res2.headers(); + if (headers.contains("Location")) { + promise.complete(headers.get("Location")); + } else { + fail("找不到重定向URL"); + } + }).onFailure(handleFail()); + } + }).onFailure(handleFail()); + + return promise.future(); + } + + + private Map extractVariables(String jsCode) { + Map variables = new HashMap<>(); + + // 正则表达式匹配 var 变量定义 + String regex = "var\\s+(\\w+)\\s*=\\s*([\"']?)([^\"';\\s]+)\\2\n"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(jsCode); + + while (matcher.find()) { + String variableName = matcher.group(1); // 变量名 + String variableValue = matcher.group(3); // 变量值 + variables.put(variableName, variableValue); + } + + return variables; + } +} diff --git a/parser/src/main/java/cn/qaiu/parser/impl/YeTool.java b/parser/src/main/java/cn/qaiu/parser/impl/YeTool.java index acb0821..1013672 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/YeTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/YeTool.java @@ -5,6 +5,7 @@ import cn.qaiu.parser.PanBase; import cn.qaiu.util.CommonUtils; import cn.qaiu.util.JsExecUtils; import io.vertx.core.Future; +import io.vertx.core.MultiMap; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.WebClient; import io.vertx.uritemplate.UriTemplate; @@ -17,6 +18,8 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static cn.qaiu.util.RandomStringGenerator.gen36String; + /** * 123网盘 */ @@ -30,8 +33,27 @@ public class YeTool extends PanBase { "&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}"; + private final MultiMap header = MultiMap.caseInsensitiveMultiMap(); + public YeTool(ShareLinkInfo shareLinkInfo) { super(shareLinkInfo); + header.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"); + header.set("App-Version", "3"); + header.set("Cache-Control", "no-cache"); + header.set("Connection", "keep-alive"); + header.set("DNT", "1"); + header.set("Host", "www.123pan.com"); + header.set("LoginUuid", gen36String()); + header.set("Pragma", "no-cache"); + header.set("Referer", shareLinkInfo.getStandardUrl()); + header.set("Sec-Fetch-Dest", "empty"); + header.set("Sec-Fetch-Mode", "cors"); + header.set("Sec-Fetch-Site", "same-origin"); + header.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0"); + header.set("platform", "web"); + header.set("sec-ch-ua", "\"Not)A;Brand\";v=\"99\", \"Microsoft Edge\";v=\"127\", \"Chromium\";v=\"127\""); + header.set("sec-ch-ua-mobile", "?0"); + header.set("sec-ch-ua-platform", "Windows"); } public Future parse() { diff --git a/parser/src/main/java/cn/qaiu/util/RandomStringGenerator.java b/parser/src/main/java/cn/qaiu/util/RandomStringGenerator.java index 49f9300..83a8ee1 100644 --- a/parser/src/main/java/cn/qaiu/util/RandomStringGenerator.java +++ b/parser/src/main/java/cn/qaiu/util/RandomStringGenerator.java @@ -1,6 +1,7 @@ package cn.qaiu.util; import java.security.SecureRandom; +import java.util.UUID; public class RandomStringGenerator { private static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789"; @@ -19,4 +20,10 @@ public class RandomStringGenerator { return sb.toString(); } + + public static String gen36String() { + String uuid = UUID.randomUUID().toString().toLowerCase(); + // 移除短横线 + return uuid.replace("-", ""); + } } diff --git a/web-service/src/main/java/cn/qaiu/lz/common/cache/CacheConfigLoader.java b/web-service/src/main/java/cn/qaiu/lz/common/cache/CacheConfigLoader.java index d6ba163..258b348 100644 --- a/web-service/src/main/java/cn/qaiu/lz/common/cache/CacheConfigLoader.java +++ b/web-service/src/main/java/cn/qaiu/lz/common/cache/CacheConfigLoader.java @@ -19,7 +19,9 @@ public class CacheConfigLoader { TYPE = config.getString("type"); Integer defaultDuration = config.getInteger("defaultDuration"); DEFAULT_DURATION = defaultDuration == null ? 60 : defaultDuration; - config.getJsonObject("duration").getMap().forEach((k,v) -> { + JsonObject duration = config.getJsonObject("duration"); + if (duration == null) return; + duration.getMap().forEach((k, v) -> { if (v == null) { CONFIGS.put(k, DEFAULT_DURATION); } else { diff --git a/web-service/src/main/java/cn/qaiu/lz/web/controller/ParserApi.java b/web-service/src/main/java/cn/qaiu/lz/web/controller/ParserApi.java index b5f33f1..beb6166 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/controller/ParserApi.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/controller/ParserApi.java @@ -93,6 +93,7 @@ public class ParserApi { return Arrays.stream(PanDomainTemplate.values()).map(pan -> new TreeMap() {{ put("name", pan.getDisplayName()); put("type", pan.name().toLowerCase()); + put("shareUrlFormat", pan.getStandardUrlTemplate()); put("url", pan.getPanDomain()); }}).collect(Collectors.toList()); } diff --git a/web-service/src/main/java/cn/qaiu/lz/web/model/CacheLinkInfo.java b/web-service/src/main/java/cn/qaiu/lz/web/model/CacheLinkInfo.java index fcb8de0..8e386f0 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/model/CacheLinkInfo.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/model/CacheLinkInfo.java @@ -3,6 +3,7 @@ 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.entity.FileInfo; import cn.qaiu.lz.common.ToJson; import io.vertx.codegen.annotations.DataObject; import io.vertx.core.json.JsonObject; @@ -48,6 +49,8 @@ public class CacheLinkInfo implements ToJson { */ private Long expiration; + private FileInfo fileInfo; + // 使用 JsonObject 构造 public CacheLinkInfo(JsonObject json) { diff --git a/web-service/src/main/java/cn/qaiu/lz/web/model/LinkInfoResp.java b/web-service/src/main/java/cn/qaiu/lz/web/model/LinkInfoResp.java index 63405c8..6780aee 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/model/LinkInfoResp.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/model/LinkInfoResp.java @@ -10,6 +10,7 @@ public class LinkInfoResp { // 解析链接 private String downLink; private String apiLink; + private String viewLink; private Integer cacheHitTotal; private Integer parserTotal; private Integer sumTotal;