From adf56cd7683f3b064f3de6e4cdc35cea665dbfe2 Mon Sep 17 00:00:00 2001 From: QAIU <736226400@qq.com> Date: Fri, 25 Oct 2024 14:38:57 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E9=85=B7=E7=8B=97=E9=9F=B3=E4=B9=90,=20?= =?UTF-8?q?=E9=85=B7=E6=88=91=E9=9F=B3=E4=B9=90,=20=E7=BD=91=E6=98=93?= =?UTF-8?q?=E4=BA=91=E9=9F=B3=E4=B9=90,=20QQ=E9=9F=B3=E4=B9=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/verticle/ReverseProxyVerticle.java | 2 +- parser/pom.xml | 79 ++++++++++++- .../src/main/java/cn/qaiu/parser/PanBase.java | 42 ++++++- .../cn/qaiu/parser/PanDomainTemplate.java | 106 +++++++++++------- .../src/main/java/cn/qaiu/parser/PanType.java | 13 --- .../java/cn/qaiu/parser/ParserCreate.java | 24 ++-- .../main/java/cn/qaiu/parser/impl/CeTool.java | 26 ++++- .../main/java/cn/qaiu/parser/impl/KdTool.java | 26 +++++ .../java/cn/qaiu/parser/impl/MmgTool.java | 3 +- .../java/cn/qaiu/parser/impl/MnesTool.java | 7 +- .../impl/{MqqTool.java => MqqsTool.java} | 57 ++++++---- .../java/cn/qaiu/parser/impl/OtherTool.java | 22 ++++ .../src/main/java/cn/qaiu/util/URLUtil.java | 13 ++- .../src/test/java/cn/qaiu/util/TestRegex.java | 5 +- .../cn/qaiu/lz/common/util/URLParamUtil.java | 4 - .../main/resources/http-tools/pan-mne.http | 15 ++- .../src/main/resources/http-tools/temp.http | 12 ++ .../src/main/resources/http-tools/test.http | 32 +++++- 18 files changed, 376 insertions(+), 112 deletions(-) delete mode 100644 parser/src/main/java/cn/qaiu/parser/PanType.java create mode 100644 parser/src/main/java/cn/qaiu/parser/impl/KdTool.java rename parser/src/main/java/cn/qaiu/parser/impl/{MqqTool.java => MqqsTool.java} (57%) create mode 100644 parser/src/main/java/cn/qaiu/parser/impl/OtherTool.java diff --git a/core/src/main/java/cn/qaiu/vx/core/verticle/ReverseProxyVerticle.java b/core/src/main/java/cn/qaiu/vx/core/verticle/ReverseProxyVerticle.java index c8a6ad0..aa95d3b 100644 --- a/core/src/main/java/cn/qaiu/vx/core/verticle/ReverseProxyVerticle.java +++ b/core/src/main/java/cn/qaiu/vx/core/verticle/ReverseProxyVerticle.java @@ -211,7 +211,7 @@ public class ReverseProxyVerticle extends AbstractVerticle { port = 80; } String originPath = url.getPath(); - LOGGER.info("Conf(path, originPath, host, port) ----> {},{},{},{}", path, originPath, host, port); + LOGGER.info("path {}, originPath {}, to {}:{}", path, originPath, host, port); // 注意这里不能origin多个代理地址, 一个实例只能代理一个origin final HttpProxy httpProxy = HttpProxy.reverseProxy(httpClient); diff --git a/parser/pom.xml b/parser/pom.xml index 765f548..bc1fff9 100644 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -10,6 +10,11 @@ parser + jar + ${project.groupId}:${project.artifactId} + NFD parser + https://qaiu.top + UTF-8 @@ -46,10 +51,39 @@ junit junit - 4.13.2 + ${junit.version} test + + + MIT License + https://opensource.org/license/mit + + + + + qaiu + qaiu00@gmail.com + https://qaiu.top + + + + scm:git@github.com:qaiu/netdisk-fast-download.git + scm:git@github.com:qaiu/netdisk-fast-download.git + git@github.com:qaiu/netdisk-fast-download.git + + + + sonatype + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + + sonatype + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + + + @@ -62,6 +96,49 @@ ${java.version} + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + true + + + + attach-sources + + + + + org.apache.maven.plugins + maven-site-plugin + 3.7.1 + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.6.0 + true + + central + true + + diff --git a/parser/src/main/java/cn/qaiu/parser/PanBase.java b/parser/src/main/java/cn/qaiu/parser/PanBase.java index ca74bc5..5addebb 100644 --- a/parser/src/main/java/cn/qaiu/parser/PanBase.java +++ b/parser/src/main/java/cn/qaiu/parser/PanBase.java @@ -2,6 +2,7 @@ package cn.qaiu.parser; import cn.qaiu.WebClientVertxInit; import cn.qaiu.entity.ShareLinkInfo; +import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.json.DecodeException; @@ -13,11 +14,14 @@ import io.vertx.ext.web.client.WebClientSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Arrays; +import java.util.Iterator; + /** * 解析器抽象类包含promise, HTTP Client, 默认失败方法等; * 新增网盘解析器需要继承该类.
*

实现类命名规则:

- *

{网盘标识}Tool, 网盘标识不超过3个字符, 可以取网盘名称首字母缩写或拼音首字母,
+ *

{网盘标识}Tool, 网盘标识不超过5个字符, 可以取网盘名称首字母缩写或拼音首字母,
* 音乐类型的解析以M开头, 例如网易云音乐Mne

*/ public abstract class PanBase implements IPanTool { @@ -73,11 +77,11 @@ public abstract class PanBase implements IPanTool { try { String s = String.format(errorMsg.replaceAll("\\{}", "%s"), args); log.error("解析异常: " + s, t.fillInStackTrace()); - promise.fail(this.getClass().getSimpleName() + ": 解析异常: " + s + " -> " + t); + promise.fail(shareLinkInfo.getPanName() + "-" + shareLinkInfo.getType() + ": 解析异常: " + s + " -> " + t); } catch (Exception e) { log.error("ErrorMsg format fail. The parameter has been discarded", e); log.error("解析异常: " + errorMsg, t.fillInStackTrace()); - promise.fail(this.getClass().getSimpleName() + ": 解析异常: " + errorMsg + " -> " + t); + promise.fail(shareLinkInfo.getPanName() + "-" + shareLinkInfo.getType() + ": 解析异常: " + errorMsg + " -> " + t); } } @@ -91,11 +95,11 @@ public abstract class PanBase implements IPanTool { try { String s = String.format(errorMsg.replaceAll("\\{}", "%s"), args); log.error("解析异常: " + s); - promise.fail(this.getClass().getSimpleName() + " - 解析异常: " + s); + promise.fail(shareLinkInfo.getPanName() + "-" + shareLinkInfo.getType() + " - 解析异常: " + s); } catch (Exception e) { log.error("ErrorMsg format fail. The parameter has been discarded", e); log.error("解析异常: " + errorMsg); - promise.fail(this.getClass().getSimpleName() + " - 解析异常: " + errorMsg); + promise.fail(shareLinkInfo.getPanName() + "-" + shareLinkInfo.getType() + " - 解析异常: " + errorMsg); } } @@ -106,7 +110,7 @@ public abstract class PanBase implements IPanTool { * @return Handler */ protected Handler handleFail(String errorMsg) { - return t -> fail(this.getClass().getSimpleName() + " - 请求异常 {}: -> {}", errorMsg, t.fillInStackTrace()); + return t -> fail(shareLinkInfo.getPanName() + "-" + shareLinkInfo.getType() + " - 请求异常 {}: -> {}", errorMsg, t.fillInStackTrace()); } @@ -128,4 +132,30 @@ public abstract class PanBase implements IPanTool { promise.complete(url); } + protected Future future() { + return promise.future(); + } + + /** + * 调用下一个解析器, 通用域名解析 + */ + protected void nextParser() { + Iterator iterator = Arrays.asList(PanDomainTemplate.values()).iterator(); + while (iterator.hasNext()) { + if (iterator.next().name().equalsIgnoreCase(shareLinkInfo.getType())) { + if (iterator.hasNext()) { + PanDomainTemplate next = iterator.next(); + log.debug("规则不匹配, 处理解析器转发: {} -> {}", shareLinkInfo.getPanName(), next.getDisplayName()); + ParserCreate.fromType(next.name()) + .fromAnyShareUrl(shareLinkInfo.getShareUrl()) + .createTool() + .parse() + .onComplete(promise); + } else { + fail("error: 没有下一个解析处理器"); + } + } + } + } + } diff --git a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java index f01b01e..eadb24d 100644 --- a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java +++ b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java @@ -4,8 +4,11 @@ import cn.qaiu.parser.impl.*; import java.util.Arrays; import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import static java.util.regex.Pattern.compile; + /** * 枚举类 PanDomainTemplate 定义了不同网盘服务的模板信息,包括: *
    @@ -20,126 +23,144 @@ import java.util.stream.Collectors; */ public enum PanDomainTemplate { + // 网盘定义 LZ("蓝奏云", - "https://([a-z0-9-]+)?\\.?lanzou[a-z]\\.com/(.+/)?(.+)", + compile("https://([a-z0-9-]+)?\\.?lanzou[a-z]\\.com/(.+/)?(?.+)"), "https://lanzoux.com/{shareKey}", LzTool.class), // https://www.feijix.com/s/ // https://share.feijipan.com/s/ FJ("小飞机网盘", - "https://(share\\.feijipan\\.com|www\\.feijix\\.com)/s/(.+)", + compile("https://(share\\.feijipan\\.com|www\\.feijix\\.com)/s/(?.+)"), "https://www.feijix.com/s/{shareKey}", FjTool.class), // https://lecloud.lenovo.com/share/ LE("联想乐云", - "https://lecloud?\\.lenovo\\.com/share/(.+)", + compile("https://lecloud?\\.lenovo\\.com/share/(?.+)"), "https://lecloud.lenovo.com/share/{shareKey}", LeTool.class), // https://v2.fangcloud.com/s/ FC("亿方云", - "https://v2\\.fangcloud\\.(com|cn)/(s|sharing)/([^/]+)", + compile("https://v2\\.fangcloud\\.(com|cn)/(s|sharing)/(?.+)"), "https://v2.fangcloud.com/s/{shareKey}", FcTool.class), // https://www.ilanzou.com/s/ IZ("蓝奏云优享", - "https://www\\.ilanzou\\.com/s/(.+)", + compile("https://www\\.ilanzou\\.com/s/(?.+)"), "https://www.ilanzou.com/s/{shareKey}", IzTool.class), // https://wx.mail.qq.com/ftn/download? QQ("QQ邮箱中转站", - "https://i?wx\\.mail\\.qq\\.com/ftn/download\\?(.+)", + compile("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("文叔叔", - "https://(f\\.ws([0-9]{2})\\.cn|www\\.wenshushu\\.cn)/f/(.+)", + compile("https://(f\\.ws([0-9]{2})\\.cn|www\\.wenshushu\\.cn)/f/(?.+)"), "https://www.wenshushu.cn/f/{shareKey}", WsTool.class), // https://www.123pan.com/s/ YE("123网盘", - "https://www\\.(123pan|123865|123684)\\.com/s/(.+)", + compile("https://www\\.(123pan|123865|123684)\\.com/s/(?.+)(.html)?"), "https://www.123pan.com/s/{shareKey}", YeTool.class), // https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={code}&isShare=1 EC("移动云空间", - "https://www\\.ecpan\\.cn/web(/%23|/#)?/yunpanProxy\\?path=.*&data=" + - "([^&]+)&isShare=1", + compile("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("奶牛快传", - "https://(.*)cowtransfer\\.com/s/(.+)", + compile("https://(.*)cowtransfer\\.com/s/(?.+)"), "https://cowtransfer.com/s/{shareKey}", CowTool.class), CT("城通网盘", - "https://474b\\.com/file/(.+)", + compile("https://474b\\.com/file/(?.+)"), "https://474b.com/file/{shareKey}", CtTool.class), // =====================音乐类解析 分享链接标志->MxxS (单歌曲/普通音质)========================== // http://163cn.tv/xxx MNES("网易云音乐分享", - "http(s)?://163cn\\.tv/(.+)", + compile("http(s)?://163cn\\.tv/(?.+)"), "http://163cn.tv/{shareKey}", MnesTool.class), - MNE("网易云音乐", - "https://music\\.163\\.com/(#/)?song\\?id=(.+)", + // https://music.163.com/#/song?id=xxx + MNE("网易云音乐歌曲详情", + compile("https://music\\.163\\.com/(#/)?song\\?id=(?.+)"), "https://music.163.com/#/song?id={shareKey}", MnesTool.MneTool.class), // https://c6.y.qq.com/base/fcgi-bin/u?__=xxx MQQS("QQ音乐分享", - "https://(.+)\\.y\\.qq\\.com/base/fcgi-bin/u\\?__=(.+)", + compile("https://(.+)\\.y\\.qq\\.com/base/fcgi-bin/u\\?__=(?.+)"), "https://c6.y.qq.com/base/fcgi-bin/u?__={shareKey}", - MqqTool.class), + MqqsTool.class), // https://y.qq.com/n/ryqq/songDetail/000XjcLg0fbRjv?songtype=0 - MQQ("QQ音乐", - "https://y\\.qq\\.com/n/ryqq/songDetail/(.+)\\?.*", + MQQ("QQ音乐歌曲详情", + compile("https://y\\.qq\\.com/n/ryqq/songDetail/(?.+)(\\?.*)?"), "https://y.qq.com/n/ryqq/songDetail/{shareKey}", - MqqTool.class), + MqqsTool.MqqTool.class), // https://t1.kugou.com/song.html?id=xxx MKGS("酷狗音乐分享", - "https://(.+)\\.kugou\\.com/song\\.html\\?id=(.+)", + compile("https://(.+)\\.kugou\\.com/song\\.html\\?id=(?.+)"), "https://t1.kugou.com/song.html?id={shareKey}", MkgsTool.class), // https://www.kugou.com/share/2bi8Fe9CSV3.html?id=2bi8Fe9CSV3#6ed9gna4" MKGS2("酷狗音乐分享2", - "https://www\\.kugou\\.com/share/(.+).html\\?.*", + compile("https://(.+)\\.kugou\\.com/share/(?.+).html.*"), "https://www.kugou.com/share/{shareKey}.html", MkgsTool.Mkgs2Tool.class), // https://www.kugou.com/mixsong/2bi8Fe9CSV3 - MKG("酷狗音乐", - "https://(.+)\\.kugou\\.com/song\\.html\\?id=(.+)", - "https://www.kugou.com/mixsong/{shareKey}", + MKG("酷狗音乐歌曲详情", + compile("https://(.+)\\.kugou\\.com/mixsong/(?.+)\\.html.*"), + "https://www.kugou.com/mixsong/{shareKey}.html", MkgsTool.MkgTool.class), - // + // https://kuwo.cn/play_detail/395500809 MKWS("酷我音乐分享*", - "https://(.+)\\.kugou\\.com/song\\.html\\?id=(.+)", - "https://t1.kugou.com/song.html?id={shareKey}", + compile("https://kuwo\\.cn/play_detail/(?.+)"), + "https://kuwo.cn/play_detail/{shareKey}", MkwTool.class), - // + // https://music.migu.cn/v3/music/song/6326951FKBJ?channelId=001002H MMGS("咪咕音乐分享", - "https://(.+)\\.kugou\\.com/song\\.html\\?id=(.+)", - "https://t1.kugou.com/song.html?id={shareKey}", + compile("https://music\\.migu\\.cn/v3/music/song/(?.+)(\\?.*)?"), + "https://music.migu.cn/v3/music/song/{shareKey}", MkwTool.class), // =====================私有盘解析========================== + + // Cloudreve自定义域名解析, 解析器CeTool兜底策略, 即任意域名如果匹配不到对应的规则, 则由CeTool统一处理, + // 如果不属于Cloudreve盘 则调用下一个自定义域名解析器, 若都处理不了则抛出异常, 这种匹配模式类似责任链 // https://pan.huang1111.cn/s/xxx // 通用域名([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,} CE("Cloudreve", - "https://([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,}/s/(.+)", - "https://{CloudreveDomainName}/s/{shareKey}", - CeTool.class); + compile("https://([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}(/s)?/(?.+)"), + "https://{any}/s/{shareKey}", + CeTool.class), + // 可道云自定义域名解析 + KD("可道云", + compile("http(s)?://([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}(/#s)?/(?.+)"), + "https://{any}/#s/{shareKey}", + KdTool.class), + // 其他自定义域名解析 + OTHER("其他网盘", + compile("http(s)?://([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}/(?.+)"), + "https://{any}/{shareKey}", + OtherTool.class); + public static final String KEY = "KEY"; // 网盘的显示名称,用于用户界面显示 private final String displayName; // 用于匹配和解析分享链接的正则表达式,保证最后一个捕捉组能匹配到分享key - private final String regexPattern; + private final Pattern pattern; + + private final String regex; // 网盘的标准链接模板,不含占位符,用于规范化分享链接 private final String standardUrlTemplate; @@ -147,10 +168,11 @@ public enum PanDomainTemplate { // 指向解析工具IPanTool实现类 private final Class toolClass; - PanDomainTemplate(String displayName, String regexPattern, String standardUrlTemplate, + PanDomainTemplate(String displayName, Pattern pattern, String standardUrlTemplate, Class toolClass) { this.displayName = displayName; - this.regexPattern = regexPattern; + this.pattern = pattern; + this.regex = pattern.pattern(); this.standardUrlTemplate = standardUrlTemplate; this.toolClass = toolClass; } @@ -159,8 +181,12 @@ public enum PanDomainTemplate { return displayName; } - public String getRegexPattern() { - return regexPattern; + public Pattern getPattern() { + return pattern; + } + + public String getRegex() { + return regex; } public String getStandardUrlTemplate() { @@ -174,7 +200,7 @@ public enum PanDomainTemplate { public static void main(String[] args) { // 校验重复 Set collect = - Arrays.stream(PanDomainTemplate.values()).map(PanDomainTemplate::getRegexPattern).collect(Collectors.toSet()); + Arrays.stream(PanDomainTemplate.values()).map(PanDomainTemplate::getRegex).collect(Collectors.toSet()); if (collect.size()QAIU - * @date 2023/6/13 4:26 - */ -public enum PanType { - LZ("lz"), - COW("cow"); - - PanType(String type) { - } -} diff --git a/parser/src/main/java/cn/qaiu/parser/ParserCreate.java b/parser/src/main/java/cn/qaiu/parser/ParserCreate.java index 38c4aee..c13d3c1 100644 --- a/parser/src/main/java/cn/qaiu/parser/ParserCreate.java +++ b/parser/src/main/java/cn/qaiu/parser/ParserCreate.java @@ -4,7 +4,8 @@ import cn.qaiu.entity.ShareLinkInfo; import org.apache.commons.lang3.StringUtils; import java.util.regex.Matcher; -import java.util.regex.Pattern; + +import static cn.qaiu.parser.PanDomainTemplate.KEY; /** @@ -34,15 +35,14 @@ public class ParserCreate { if (StringUtils.isEmpty(shareUrl)) { throw new IllegalArgumentException("ShareLinkInfo shareUrl is empty"); } - Pattern pattern = Pattern.compile(this.panDomainTemplate.getRegexPattern()); - Matcher matcher = pattern.matcher(shareUrl); + Matcher matcher = this.panDomainTemplate.getPattern().matcher(shareUrl); if (matcher.find()) { - String shareKey = matcher.group(matcher.groupCount()); + String shareKey = matcher.group(KEY); // 返回规范化的标准链接 String standardUrl = getStandardUrlTemplate().replace("{shareKey}", shareKey); shareLinkInfo.setShareUrl(shareUrl); shareLinkInfo.setShareKey(shareKey); - if (!(panDomainTemplate == PanDomainTemplate.CE)) { + if (!(panDomainTemplate.ordinal() >= PanDomainTemplate.CE.ordinal())) { shareLinkInfo.setStandardUrl(standardUrl); } return this; @@ -68,7 +68,7 @@ public class ParserCreate { // set share key public ParserCreate shareKey(String shareKey) { - if (panDomainTemplate == PanDomainTemplate.CE) { + if (panDomainTemplate.ordinal() >= PanDomainTemplate.CE.ordinal()) { // 处理Cloudreve(ce)类: pan.huang1111.cn_s_wDz5TK _ -> / String[] s = shareKey.split("_"); String standardUrl = "https://" + String.join("/", s); @@ -82,6 +82,13 @@ public class ParserCreate { return this; } + // set any share url + public ParserCreate fromAnyShareUrl(String url) { + shareLinkInfo.setStandardUrl(url); + shareLinkInfo.setShareUrl(url); + return this; + } + public String getStandardUrlTemplate() { return this.panDomainTemplate.getStandardUrlTemplate(); } @@ -98,12 +105,12 @@ public class ParserCreate { // 根据分享链接获取PanDomainTemplate实例 public synchronized static ParserCreate fromShareUrl(String shareUrl) { for (PanDomainTemplate panDomainTemplate : PanDomainTemplate.values()) { - if (shareUrl.matches(panDomainTemplate.getRegexPattern())) { + if (panDomainTemplate.getPattern().matcher(shareUrl).matches()) { ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder() .type(panDomainTemplate.name().toLowerCase()) .panName(panDomainTemplate.getDisplayName()) .shareUrl(shareUrl).build(); - if (panDomainTemplate == PanDomainTemplate.CE) { + if (panDomainTemplate.ordinal() >= PanDomainTemplate.CE.ordinal()) { shareLinkInfo.setStandardUrl(shareUrl); } ParserCreate parserCreate = new ParserCreate(panDomainTemplate, shareLinkInfo); @@ -119,6 +126,7 @@ public class ParserCreate { PanDomainTemplate panDomainTemplate = Enum.valueOf(PanDomainTemplate.class, type.toUpperCase()); ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder() .type(type.toLowerCase()).build(); + shareLinkInfo.setPanName(panDomainTemplate.getDisplayName()); return new ParserCreate(panDomainTemplate, shareLinkInfo); } catch (IllegalArgumentException ignore) { // 如果没有找到对应的枚举实例,抛出异常 diff --git a/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java b/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java index 88245a7..666b17e 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/CeTool.java @@ -1,13 +1,17 @@ package cn.qaiu.parser.impl; -import cn.qaiu.entity.ShareLinkInfo; +import cn.qaiu.entity.ShareLinkInfo; import cn.qaiu.parser.PanBase; +import cn.qaiu.parser.PanDomainTemplate; +import cn.qaiu.parser.ParserCreate; import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.HttpRequest; import java.net.URL; +import java.util.Arrays; +import java.util.Iterator; /** * Cloudreve自建网盘解析
    @@ -42,22 +46,32 @@ public class CeTool extends PanBase { String downloadApiUrl = url.getProtocol() + "://" + url.getHost() + DOWNLOAD_API_PATH + key + "?path" + "=undefined/undefined;"; String shareApiUrl = url.getProtocol() + "://" + url.getHost() + SHARE_API_PATH + key; - // 设置cookie HttpRequest httpRequest = clientSession.getAbs(shareApiUrl); if (pwd != null) { httpRequest.addQueryParam("password", pwd); } // 获取下载链接 - httpRequest.send().onSuccess(res -> getDownURL(downloadApiUrl)).onFailure(handleFail(shareApiUrl)); + httpRequest.send().onSuccess(res -> { + try { + if (res.statusCode() == 200 && res.bodyAsJsonObject().containsKey("code")) { + getDownURL(downloadApiUrl); + } else { + nextParser(); + } + } catch (Exception e) { + nextParser(); + } + }).onFailure(handleFail(shareApiUrl)); } catch (Exception e) { fail(e, "URL解析错误"); } return promise.future(); } - private void getDownURL(String apiUrl) { - clientSession.putAbs(apiUrl).send().onSuccess(res -> { + + private void getDownURL(String shareApiUrl) { + clientSession.putAbs(shareApiUrl).send().onSuccess(res -> { JsonObject jsonObject = asJson(res); System.out.println(jsonObject.encodePrettily()); if (jsonObject.containsKey("code") && jsonObject.getInteger("code") == 0) { @@ -65,6 +79,6 @@ public class CeTool extends PanBase { } else { fail("JSON解析失败: {}", jsonObject.encodePrettily()); } - }).onFailure(handleFail(apiUrl)); + }).onFailure(handleFail(shareApiUrl)); } } diff --git a/parser/src/main/java/cn/qaiu/parser/impl/KdTool.java b/parser/src/main/java/cn/qaiu/parser/impl/KdTool.java new file mode 100644 index 0000000..83a5da0 --- /dev/null +++ b/parser/src/main/java/cn/qaiu/parser/impl/KdTool.java @@ -0,0 +1,26 @@ +package cn.qaiu.parser.impl; + +import cn.qaiu.entity.ShareLinkInfo; +import cn.qaiu.parser.PanBase; +import io.vertx.core.Future; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +import java.util.UUID; + +/** + * 可道云 + */ +public class KdTool extends PanBase { + private static final String API_URL_PREFIX = ""; + + public KdTool(ShareLinkInfo shareLinkInfo) { + super(shareLinkInfo); + } + + public Future parse() { + nextParser(); + // TODO + return future(); + } +} diff --git a/parser/src/main/java/cn/qaiu/parser/impl/MmgTool.java b/parser/src/main/java/cn/qaiu/parser/impl/MmgTool.java index 07aa2cc..9918497 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/MmgTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/MmgTool.java @@ -3,7 +3,6 @@ package cn.qaiu.parser.impl; import cn.qaiu.entity.ShareLinkInfo; import cn.qaiu.parser.PanBase; import io.vertx.core.Future; -import io.vertx.uritemplate.UriTemplate; /** * 咪咕音乐分享 @@ -20,6 +19,8 @@ public class MmgTool extends PanBase { public Future parse() { String shareUrl = shareLinkInfo.getStandardUrl(); + // TODO + promise.complete("暂未实现, 敬请期待"); return promise.future(); } } diff --git a/parser/src/main/java/cn/qaiu/parser/impl/MnesTool.java b/parser/src/main/java/cn/qaiu/parser/impl/MnesTool.java index fbebd99..058a5c5 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/MnesTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/MnesTool.java @@ -34,7 +34,12 @@ public class MnesTool extends PanBase { String id = URLUtil.from(locationURL).getParam("id"); clientNoRedirects.getAbs(UriTemplate.of(API_URL)).setTemplateParam("id", id).send() .onSuccess(res2 -> { - promise.complete(res2.headers().get("Location")); + String location = res2.headers().get("Location"); + if (location.endsWith("/404")) { + fail("链接已失效: id={}", id); + } else { + promise.complete(location); + } }).onFailure(handleFail(API_URL.replace("{id}", id))); } diff --git a/parser/src/main/java/cn/qaiu/parser/impl/MqqTool.java b/parser/src/main/java/cn/qaiu/parser/impl/MqqsTool.java similarity index 57% rename from parser/src/main/java/cn/qaiu/parser/impl/MqqTool.java rename to parser/src/main/java/cn/qaiu/parser/impl/MqqsTool.java index 48a7560..6d1da84 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/MqqTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/MqqsTool.java @@ -14,7 +14,7 @@ import io.vertx.uritemplate.UriTemplate; * 分享示例 * 详情页 */ -public class MqqTool extends PanBase { +public class MqqsTool extends PanBase { public static final String API_URL = "https://u.y.qq.com/cgi-bin/musicu" + ".fcg?-=getplaysongvkey2682247447678878&g_tk=5381&loginUin=956581739&hostUin=0&format=json&inCharset=utf8" + @@ -24,7 +24,7 @@ public class MqqTool extends PanBase { "%2C%22loginflag%22%3A1%2C%22platform%22%3A%2220%22%7D%7D%2C%22comm%22%3A%7B%22uin%22%3A956581739%2C" + "%22format%22%3A%22json%22%2C%22ct%22%3A24%2C%22cv%22%3A0%7D%7D"; - public MqqTool(ShareLinkInfo shareLinkInfo) { + public MqqsTool(ShareLinkInfo shareLinkInfo) { super(shareLinkInfo); } @@ -36,30 +36,43 @@ public class MqqTool extends PanBase { clientNoRedirects.getAbs(shareUrl).send().onSuccess(res -> { String locationURL = res.headers().get("Location"); String id = URLUtil.from(locationURL).getParam("songmid"); - clientNoRedirects.getAbs(UriTemplate.of(API_URL)).setTemplateParam("songmid", id).send().onSuccess(res2 -> { - JsonObject jsonObject = asJson(res2); - System.out.println(jsonObject.encodePrettily()); - try { - JsonObject data = jsonObject.getJsonObject("req_0").getJsonObject("data"); - String path = data.getJsonArray("midurlinfo").getJsonObject(0).getString("purl"); - if (path.isEmpty()) { - fail("暂不支持VIP音乐"); - return; - } - String downURL = data.getJsonArray("sip").getString(0) - .replace("http://", "https://") + path; - System.out.println(downURL); - promise.complete(downURL); - } catch (Exception e) { - fail("获取失败"); - } - }).onFailure(handleFail(API_URL.replace("{id}", id))); + downUrl(id); }).onFailure(handleFail(shareUrl)); return promise.future(); } - public static void main(String[] args) { - new MqqTool(ShareLinkInfo.newBuilder().build()).parse(); + protected void downUrl(String id) { + clientNoRedirects.getAbs(UriTemplate.of(API_URL)).setTemplateParam("songmid", id).send().onSuccess(res2 -> { + JsonObject jsonObject = asJson(res2); + log.debug(jsonObject.encodePrettily()); + try { + JsonObject data = jsonObject.getJsonObject("req_0").getJsonObject("data"); + String path = data.getJsonArray("midurlinfo").getJsonObject(0).getString("purl"); + if (path.isEmpty()) { + fail("暂不支持VIP音乐"); + return; + } + String downURL = data.getJsonArray("sip").getString(0) + .replace("http://", "https://") + path; + promise.complete(downURL); + } catch (Exception e) { + fail("获取失败"); + } + }).onFailure(handleFail(API_URL.replace("{id}", id))); + } + + + public static class MqqTool extends MqqsTool{ + + public MqqTool(ShareLinkInfo shareLinkInfo) { + super(shareLinkInfo); + } + + @Override + public Future parse() { + downUrl(shareLinkInfo.getShareKey()); + return promise.future(); + } } } diff --git a/parser/src/main/java/cn/qaiu/parser/impl/OtherTool.java b/parser/src/main/java/cn/qaiu/parser/impl/OtherTool.java new file mode 100644 index 0000000..6905727 --- /dev/null +++ b/parser/src/main/java/cn/qaiu/parser/impl/OtherTool.java @@ -0,0 +1,22 @@ +package cn.qaiu.parser.impl; + +import cn.qaiu.entity.ShareLinkInfo; +import cn.qaiu.parser.PanBase; +import io.vertx.core.Future; + +/** + * 其他网盘解析 + */ +public class OtherTool extends PanBase { + private static final String API_URL_PREFIX = ""; + + public OtherTool(ShareLinkInfo shareLinkInfo) { + super(shareLinkInfo); + } + + public Future parse() { + // TODO + fail("暂未实现, 敬请期待"); + return future(); + } +} diff --git a/parser/src/main/java/cn/qaiu/util/URLUtil.java b/parser/src/main/java/cn/qaiu/util/URLUtil.java index 1b38a31..916a27f 100644 --- a/parser/src/main/java/cn/qaiu/util/URLUtil.java +++ b/parser/src/main/java/cn/qaiu/util/URLUtil.java @@ -1,25 +1,32 @@ package cn.qaiu.util; +import org.apache.commons.lang3.StringUtils; + import java.net.URL; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; public class URLUtil { - private Map queryParams = new HashMap<>(); + private final Map queryParams = new HashMap<>(); // 构造函数,传入URL并解析参数 private URLUtil(String url) { try { URL parsedUrl = new URL(url); + String ref = parsedUrl.getRef(); + if (StringUtils.isNotEmpty(ref)) { + parsedUrl = new URL(parsedUrl.getProtocol() + "://" + parsedUrl.getHost() + ref); + } String query = parsedUrl.getQuery(); if (query != null) { String[] pairs = query.split("&"); for (String pair : pairs) { String[] keyValue = pair.split("="); - String key = URLDecoder.decode(keyValue[0], "UTF-8"); - String value = keyValue.length > 1 ? URLDecoder.decode(keyValue[1], "UTF-8") : ""; + String key = URLDecoder.decode(keyValue[0], StandardCharsets.UTF_8); + String value = keyValue.length > 1 ? URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8) : ""; queryParams.put(key, value); } } diff --git a/parser/src/test/java/cn/qaiu/util/TestRegex.java b/parser/src/test/java/cn/qaiu/util/TestRegex.java index 610f834..90e7916 100644 --- a/parser/src/test/java/cn/qaiu/util/TestRegex.java +++ b/parser/src/test/java/cn/qaiu/util/TestRegex.java @@ -10,11 +10,10 @@ public class TestRegex { @Test public void regexYFC() { String html = """ - - + https://www.kugou.com/mixsong/9q98o5b9.html """; - Pattern compile = Pattern.compile("id=\"typed_id\"\\s+value=\"file_(\\d+)\""); + Pattern compile = Pattern.compile("https://(.+)\\.kugou\\.com/mixsong/(?.+).html"); Matcher matcher = compile.matcher(html); if (matcher.find()) { System.out.println(matcher.group(0)); diff --git a/web-service/src/main/java/cn/qaiu/lz/common/util/URLParamUtil.java b/web-service/src/main/java/cn/qaiu/lz/common/util/URLParamUtil.java index 168561e..66972a0 100644 --- a/web-service/src/main/java/cn/qaiu/lz/common/util/URLParamUtil.java +++ b/web-service/src/main/java/cn/qaiu/lz/common/util/URLParamUtil.java @@ -28,10 +28,6 @@ public class URLParamUtil { if (params.contains("url")) { String encodedUrl = params.get("url"); url = handleTruncatedUrl(encodedUrl, params); - if (url.endsWith(".html")) { - // 123云盘的后缀处理 - url = url.replace(".html", ""); - } } return url; } diff --git a/web-service/src/main/resources/http-tools/pan-mne.http b/web-service/src/main/resources/http-tools/pan-mne.http index 647c121..01c2ad4 100644 --- a/web-service/src/main/resources/http-tools/pan-mne.http +++ b/web-service/src/main/resources/http-tools/pan-mne.http @@ -8,7 +8,10 @@ https://music.163.com/song?id=233334 ### #@no-redirect -https://music.163.com/song/media/outer/url?id=233334 +https://music.163.com/song/media/outer/url?id=2020593612 + +### +https://music.163.com/#/song?id=092087 ### @@ -166,3 +169,13 @@ https://ws6.stream.qqmusic.qq.com/C400003mAan70zUy5O.m4a?guid=2796982635&vkey=B2 ### https://ws.stream.qqmusic.qq.com/C400003mAan70zUy5O.m4a?fromtag=666&guid=2796982635&vkey=B2EDD08E318F0C0D2B3A9462FC5754CBCA9AEFD796FA0662C83A102821425D31547F957451751901F195095830842E1565FF8815B210B25A + +### +https://y.qq.com/n/ryqq/songDetail/000KKjzb2Eq15b + +### +# @no-redirect +https://c6.y.qq.com/base/fcgi-bin/u?__=k8gafY6HAQ5Y + +### +https://u.y.qq.com/cgi-bin/musicu.fcg?-=getplaysongvkey2682247447678878&g_tk=5381&loginUin=956581739&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0&data=%7B%22req_0%22%3A%7B%22module%22%3A%22vkey.GetVkeyServer%22%2C%22method%22%3A%22CgiGetVkey%22%2C%22param%22%3A%7B%22guid%22%3A%222796982635%22%2C%22songmid%22%3A%5B%22000KKjzb2Eq15b%22%5D%2C%22songtype%22%3A%5B1%5D%2C%22uin%22%3A%22956581739%22%2C%22loginflag%22%3A1%2C%22platform%22%3A%2220%22%7D%7D%2C%22comm%22%3A%7B%22uin%22%3A956581739%2C%22format%22%3A%22json%22%2C%22ct%22%3A24%2C%22cv%22%3A0%7D%7D diff --git a/web-service/src/main/resources/http-tools/temp.http b/web-service/src/main/resources/http-tools/temp.http index 7cc3e5d..619e726 100644 --- a/web-service/src/main/resources/http-tools/temp.http +++ b/web-service/src/main/resources/http-tools/temp.http @@ -64,3 +64,15 @@ sec-fetch-user: ?1 upgrade-insecure-requests: 1 user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Content-Type: application/x-www-form-urlencoded + +### +GET https://blog.qaiu.top/api/v3/share/download/nLNsQ + +### +# @no-redirect +GET http://127.0.0.1:6401/parser?url=https://blog.qaiu.top/s/nLNsQ1 + +### +# @no-redirect +GET http://127.0.0.1:6401/parser?url=https://pan.seeoss.com/s/nLNsQ1 + diff --git a/web-service/src/main/resources/http-tools/test.http b/web-service/src/main/resources/http-tools/test.http index 6ed1fa5..e493e68 100644 --- a/web-service/src/main/resources/http-tools/test.http +++ b/web-service/src/main/resources/http-tools/test.http @@ -153,8 +153,14 @@ GET http://127.0.0.1:6400/json/ce/pan.huang1111.cn_s_g31PcQ@qaiu #GET http://127.0.0.1:6400/parser?url=https://pan.huang1111.cn/s/g31PcQ&pwd=qaiu # @no-redirect -GET http://127.0.0.1:6400/parser?url=https://pan.seeoss.com/s/nLNsQ +GET http://127.0.0.1:6401/parser?url=https://pan.seeoss.com/s/nLNsQ +### +# @no-redirect +GET http://127.0.0.1:6401/parser?url=https://blog.qaiu.top/s/nLNsQ + +### +PUT https://blog.qaiu.top/s/nLNsQ ### PASS QQ @@ -197,15 +203,37 @@ GET http://127.0.0.1:6401/parser?url=https://474b.com/file/4015376-131945810 ### PASS MNE # @no-redirect -GET http://127.0.0.1:6401/parser?url=http://163cn.tv/ykLZJJT +GET http://127.0.0.1:6401/json/parser?url=http://163cn.tv/ykLZJJT +### PASS MNE2 +# @no-redirect +GET http://127.0.0.1:6401/parser?url=https://music.163.com/#/song?id=472194327 + ### PASS MQQ # @no-redirect GET http://127.0.0.1:6401/parser?url=https://c6.y.qq.com/base/fcgi-bin/u?__=k8gafY6HAQ5Y +### PASS MQQ2 +# @no-redirect +GET http://127.0.0.1:6401/parser?url=https://y.qq.com/n/ryqq/songDetail/000KKjzb2Eq15b +### PASS MKG0 酷狗 +# @no-redirect +GET http://127.0.0.1:6401/parser?url=https://www.kugou.com/share/2bi8Fe9CSV3.html ### PASS MKG # @no-redirect +GET http://127.0.0.1:6401/json/parser?url=https://www.kugou.com/share/2bi8Fe9CSV3.html?id=2bi8Fe9CSV3#6ed9gna4 +### PASS MKG2 +# @no-redirect GET http://127.0.0.1:6401/parser?url=https://t1.kugou.com/song.html?id=2bi8Fe9CSV3 +### PASS MKG3 +# @no-redirect +GET http://127.0.0.1:6401/parser?url=https://www.kugou.com/mixsong/9q98o5b9.html + +### PASS MQW 酷我 +# @no-redirect +GET http://127.0.0.1:6401/parser?url=https://kuwo.cn/play_detail/395500809 + + ### n1 http://127.0.0.1:6401/n1/statisticsInfo