mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 20:33:03 +00:00
1. add music parser
This commit is contained in:
@@ -15,7 +15,10 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 解析器抽象类包含promise, HTTP Client, 默认失败方法等;
|
||||
* 新增网盘解析器需要继承该类.
|
||||
* 新增网盘解析器需要继承该类. <br>
|
||||
* <h2>实现类命名规则: </h2>
|
||||
* <p>{网盘标识}Tool, 网盘标识不超过3个字符, 可以取网盘名称首字母缩写或拼音首字母, <br>
|
||||
* 音乐类型的解析以M开头, 例如网易云音乐Mne</p>
|
||||
*/
|
||||
public abstract class PanBase implements IPanTool {
|
||||
protected Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
@@ -55,6 +58,10 @@ public abstract class PanBase implements IPanTool {
|
||||
this.shareLinkInfo = shareLinkInfo;
|
||||
}
|
||||
|
||||
protected PanBase() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 失败时生成异常消息
|
||||
*
|
||||
|
||||
@@ -80,6 +80,16 @@ public enum PanDomainTemplate {
|
||||
"http(s)?://163cn\\.tv/(.+)",
|
||||
"http://163cn.tv/{shareKey}",
|
||||
MneTool.class),
|
||||
// https://c6.y.qq.com/base/fcgi-bin/u?__=xxx
|
||||
MQQ("QQ音乐",
|
||||
"https://(.+)\\.y\\.qq\\.com/base/fcgi-bin/u\\?__=(.+)",
|
||||
"https://c6.y.qq.com/base/fcgi-bin/u?__={shareKey}",
|
||||
MqqTool.class),
|
||||
// https://t1.kugou.com/song.html?id=xxx
|
||||
MKG("酷狗音乐",
|
||||
"https://(.+)\\.kugou\\.com/song\\.html\\?id=(.+)",
|
||||
"https://t1.kugou.com/song.html?id={shareKey}",
|
||||
MkgTool.class),
|
||||
// https://pan.huang1111.cn/s/xxx
|
||||
// 通用域名([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}
|
||||
CE("Cloudreve",
|
||||
|
||||
97
parser/src/main/java/cn/qaiu/parser/impl/MkgTool.java
Normal file
97
parser/src/main/java/cn/qaiu/parser/impl/MkgTool.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.URLUtil;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 酷狗音乐分享
|
||||
*/
|
||||
public class MkgTool extends PanBase {
|
||||
|
||||
public static final String API_URL = "https://m.kugou.com/app/i/getSongInfo.php?cmd=playInfo&hash={hash}";
|
||||
|
||||
private static final MultiMap headers = MultiMap.caseInsensitiveMultiMap();
|
||||
static {
|
||||
// 设置 User-Agent
|
||||
headers.set("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 Edg/129.0.0.0");
|
||||
|
||||
// 设置 Accept
|
||||
headers.set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
|
||||
|
||||
// 设置 If-Modified-Since
|
||||
headers.set("If-Modified-Since", "Mon, 21 Oct 2024 08:45:50 GMT");
|
||||
|
||||
// 设置 Priority
|
||||
headers.set("Priority", "u=0, i");
|
||||
|
||||
// 设置 Sec-CH-UA
|
||||
headers.set("Sec-CH-UA", "\"Microsoft Edge\";v=\"129\", \"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"129\"");
|
||||
|
||||
// 设置 Sec-CH-UA-Mobile
|
||||
headers.set("Sec-CH-UA-Mobile", "?0");
|
||||
|
||||
// 设置 Sec-CH-UA-Platform
|
||||
headers.set("Sec-CH-UA-Platform", "\"Windows\"");
|
||||
|
||||
// 设置 Sec-Fetch-Dest
|
||||
headers.set("Sec-Fetch-Dest", "document");
|
||||
|
||||
// 设置 Sec-Fetch-Mode
|
||||
headers.set("Sec-Fetch-Mode", "navigate");
|
||||
|
||||
// 设置 Sec-Fetch-Site
|
||||
headers.set("Sec-Fetch-Site", "none");
|
||||
|
||||
// 设置 Sec-Fetch-User
|
||||
headers.set("Sec-Fetch-User", "?1");
|
||||
|
||||
// 设置 Upgrade-Insecure-Requests
|
||||
headers.set("Upgrade-Insecure-Requests", "1");
|
||||
};
|
||||
|
||||
public MkgTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
String shareUrl = shareLinkInfo.getStandardUrl();
|
||||
// String shareUrl = "https://t1.kugou.com/song.html?id=2bi8Fe9CSV3";
|
||||
clientNoRedirects.getAbs(shareUrl).send().onSuccess(res -> {
|
||||
String locationURL = res.headers().get("Location");
|
||||
client.getAbs(locationURL).putHeaders(headers).send().onSuccess(res2->{
|
||||
String body = res2.bodyAsString();
|
||||
// 正则表达式匹配 hash 字段
|
||||
String regex = "\"hash\"\s*:\s*\"([A-F0-9]+)\"";
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher matcher = pattern.matcher(body);
|
||||
|
||||
// 查找并输出 hash 字段的值
|
||||
if (matcher.find()) {
|
||||
String hashValue = matcher.group(1); // 获取第一个捕获组
|
||||
System.out.println(hashValue);
|
||||
client.getAbs(UriTemplate.of(API_URL)).setTemplateParam("hash", hashValue).send().onSuccess(res3 -> {
|
||||
JsonObject jsonObject = asJson(res3);
|
||||
System.out.println(jsonObject.encodePrettily());
|
||||
if (jsonObject.containsKey("url")) {
|
||||
promise.complete(jsonObject.getString("url"));
|
||||
} else {
|
||||
fail("下载链接不存在");
|
||||
}
|
||||
}).onFailure(handleFail(API_URL.replace("{hash}", hashValue)));
|
||||
} else {
|
||||
fail("歌曲hash匹配失败, 可能分享已失效");
|
||||
}
|
||||
}).onFailure(handleFail(locationURL));
|
||||
}).onFailure(handleFail(shareUrl));
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
}
|
||||
62
parser/src/main/java/cn/qaiu/parser/impl/MkwTool.java
Normal file
62
parser/src/main/java/cn/qaiu/parser/impl/MkwTool.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.JsExecUtils;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 酷我音乐分享
|
||||
* <a href="https://kuwo.cn/play_detail/395500809">分享示例</a>
|
||||
*/
|
||||
public class MkwTool extends PanBase {
|
||||
|
||||
public static final String API_URL = "https://www.kuwo.cn/api/v1/www/music/playUrl?mid={mid}&type=music&httpsStatus=1&reqId=&plat=web_www&from=";
|
||||
|
||||
|
||||
public MkwTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
// String shareUrl = shareLinkInfo.getStandardUrl();
|
||||
String shareUrl = "https://kuwo.cn/play_detail/395500809";
|
||||
clientSession.getAbs(shareUrl).send().onSuccess(result -> {
|
||||
String cookie = result.headers().get("set-cookie");
|
||||
|
||||
if (!cookie.isEmpty()) {
|
||||
|
||||
String regex = "([A-Za-z0-9_]+)=([A-Za-z0-9]+)";
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher matcher = pattern.matcher(cookie);
|
||||
if (matcher.find()) {
|
||||
System.out.println(matcher.group(1));
|
||||
System.out.println(matcher.group(2));
|
||||
|
||||
var key = matcher.group(1);
|
||||
var token = matcher.group(2);
|
||||
String sign = JsExecUtils.getKwSign(token, key);
|
||||
System.out.println(sign);
|
||||
clientSession.getAbs(UriTemplate.of(API_URL)).setQueryParam("mid", "395500809")
|
||||
.putHeader("Secret", sign).putHeader("Cookie", key + "=" + token).send().onSuccess(res -> {
|
||||
System.out.println(res.bodyAsString());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
MkwTool() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new MkwTool().parse();
|
||||
}
|
||||
}
|
||||
25
parser/src/main/java/cn/qaiu/parser/impl/MmgTool.java
Normal file
25
parser/src/main/java/cn/qaiu/parser/impl/MmgTool.java
Normal file
@@ -0,0 +1,25 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 咪咕音乐分享
|
||||
*/
|
||||
public class MmgTool extends PanBase {
|
||||
|
||||
public static final String API_URL = "";
|
||||
|
||||
|
||||
public MmgTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
String shareUrl = shareLinkInfo.getStandardUrl();
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,14 @@ package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.URLUtil;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
/**
|
||||
* 网易云音乐, 单歌曲直链解析
|
||||
* <a href="http://163cn.tv/ykLZJJT">示例分享</a>
|
||||
* <a href="http://163cn.tv/ykLZJJT">示例分享1</a>
|
||||
* <a href="https://music.163.com/#/song?id=472194327">示例分享2</a>
|
||||
*/
|
||||
public class MneTool extends PanBase {
|
||||
|
||||
@@ -23,8 +25,7 @@ public class MneTool extends PanBase {
|
||||
String shareUrl = shareLinkInfo.getStandardUrl();
|
||||
clientNoRedirects.getAbs(shareUrl).send().onSuccess(res -> {
|
||||
String locationURL = res.headers().get("Location");
|
||||
String substring = locationURL.substring(locationURL.indexOf("id="));
|
||||
String id = substring.substring("id=".length(), substring.indexOf('&'));
|
||||
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"));
|
||||
|
||||
65
parser/src/main/java/cn/qaiu/parser/impl/MqqTool.java
Normal file
65
parser/src/main/java/cn/qaiu/parser/impl/MqqTool.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package cn.qaiu.parser.impl;
|
||||
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.util.URLUtil;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.uritemplate.UriTemplate;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* QQ音乐分享解析
|
||||
* <a href="https://c6.y.qq.com/base/fcgi-bin/u?__=w3lqEpOHACLO">分享示例</a>
|
||||
* <a href="https://y.qq.com/n/ryqq/songDetail/000XjcLg0fbRjv?songtype=0">详情页</a>
|
||||
*/
|
||||
public class MqqTool 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" +
|
||||
"&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%22{songmid}%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";
|
||||
|
||||
public MqqTool(ShareLinkInfo shareLinkInfo) {
|
||||
super(shareLinkInfo);
|
||||
}
|
||||
|
||||
public Future<String> parse() {
|
||||
String shareUrl = shareLinkInfo.getStandardUrl();
|
||||
// https://c6.y.qq.com/base/fcgi-bin/u?__=uXXtsB
|
||||
// String shareUrl = "https://c6.y.qq.com/base/fcgi-bin/u?__=k8gafY6HAQ5Y";
|
||||
|
||||
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)));
|
||||
}).onFailure(handleFail(shareUrl));
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new MqqTool(ShareLinkInfo.newBuilder().build()).parse();
|
||||
}
|
||||
}
|
||||
@@ -329,4 +329,73 @@ public class AESUtils {
|
||||
return _0x53928f + "-" + _0x430930 + "-" + _0x49ec94;
|
||||
}
|
||||
|
||||
|
||||
public static String encrypt(String str, String pwd) {
|
||||
if (pwd == null || pwd.length() <= 0) {
|
||||
throw new IllegalArgumentException("Please enter a password with which to encrypt the message.");
|
||||
}
|
||||
|
||||
// 生成 prand 值
|
||||
StringBuilder prand = new StringBuilder();
|
||||
for (int i = 0; i < pwd.length(); i++) {
|
||||
prand.append((int) pwd.charAt(i));
|
||||
}
|
||||
|
||||
// 计算 sPos, mult, incr, modu
|
||||
int sPos = prand.length() / 5;
|
||||
long mult = Long.parseLong(prand.substring(sPos, sPos + 1) +
|
||||
prand.substring(sPos * 2, sPos * 2 + 1) +
|
||||
prand.substring(sPos * 3, sPos * 3 + 1) +
|
||||
prand.substring(sPos * 4, sPos * 4 + 1) +
|
||||
prand.substring(sPos * 5, sPos * 5 + 1));
|
||||
int incr = (int) Math.ceil(pwd.length() / 2.0);
|
||||
long modu = (long) Math.pow(2, 31) - 1;
|
||||
|
||||
if (mult < 2) {
|
||||
throw new IllegalArgumentException("Algorithm cannot find a suitable hash. Please choose a different password.");
|
||||
}
|
||||
|
||||
// 生成 salt 并加到 prand 上
|
||||
long salt = Math.round(Math.random() * 1000000000) % 100000000;
|
||||
prand.append(salt);
|
||||
|
||||
// 使用 BigInteger 处理超过 Long 范围的 prand 值
|
||||
BigInteger prandValue = new BigInteger(prand.toString());
|
||||
while (prandValue.toString().length() > 10) {
|
||||
prandValue = prandValue.divide(BigInteger.TEN).add(prandValue.remainder(BigInteger.TEN));
|
||||
}
|
||||
|
||||
// 将 prand 转换为 long 进行后续处理
|
||||
prandValue = prandValue.multiply(BigInteger.valueOf(mult)).add(BigInteger.valueOf(incr)).mod(BigInteger.valueOf(modu));
|
||||
|
||||
// 加密过程
|
||||
StringBuilder encStr = new StringBuilder();
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
// 将字符和异或的结果强制转换为 int
|
||||
int encChr = (int) (str.charAt(i) ^ (int) ((prandValue.doubleValue() / modu) * 255));
|
||||
String hexStr = Integer.toHexString(encChr);
|
||||
if (hexStr.length() < 2) {
|
||||
encStr.append("0");
|
||||
}
|
||||
encStr.append(hexStr);
|
||||
|
||||
prandValue = prandValue.multiply(BigInteger.valueOf(mult)).add(BigInteger.valueOf(incr)).mod(BigInteger.valueOf(modu));
|
||||
}
|
||||
|
||||
// 将 salt 转换为 16 进制并追加到加密结果
|
||||
String saltHex = Long.toHexString(salt);
|
||||
while (saltHex.length() < 8) {
|
||||
saltHex = "0" + saltHex;
|
||||
}
|
||||
encStr.append(saltHex);
|
||||
|
||||
return encStr.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 示例
|
||||
String encrypted = encrypt("HelloWorld", "password123");
|
||||
System.out.println("Encrypted String: " + encrypted);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -149,4 +149,41 @@ public interface JsContent {
|
||||
|
||||
""";
|
||||
|
||||
String kwSignString = """
|
||||
function encrypt(str, pwd) {
|
||||
if (pwd == null || pwd.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
var prand = "";
|
||||
for (var i = 0; i < pwd.length; i++) {
|
||||
prand += pwd.charCodeAt(i).toString();
|
||||
}
|
||||
var sPos = Math.floor(prand.length / 5);
|
||||
var mult = parseInt(prand.charAt(sPos) + prand.charAt(sPos * 2) + prand.charAt(sPos * 3) + prand.charAt(sPos * 4) + prand.charAt(sPos * 5));
|
||||
var incr = Math.ceil(pwd.length / 2);
|
||||
var modu = Math.pow(2, 31) - 1;
|
||||
if (mult < 2) {
|
||||
return null;
|
||||
}
|
||||
var salt = Math.round(Math.random() * 1000000000) % 100000000;
|
||||
prand += salt;
|
||||
while (prand.length > 10) {
|
||||
prand = (parseInt(prand.substring(0, 10)) + parseInt(prand.substring(10, prand.length))).toString();
|
||||
}
|
||||
prand = (mult * prand + incr) % modu;
|
||||
var enc_chr = "";
|
||||
var enc_str = "";
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
enc_chr = parseInt(str.charCodeAt(i) ^ Math.floor((prand / modu) * 255));
|
||||
if (enc_chr < 16) {
|
||||
enc_str += "0" + enc_chr.toString(16);
|
||||
} else enc_str += enc_chr.toString(16);
|
||||
prand = (mult * prand + incr) % modu;
|
||||
}
|
||||
salt = salt.toString(16);
|
||||
while (salt.length < 8) salt = "0" + salt;
|
||||
enc_str += salt;
|
||||
return enc_str;
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import static cn.qaiu.util.AESUtils.encrypt;
|
||||
|
||||
/**
|
||||
* 执行Js脚本
|
||||
*
|
||||
@@ -57,4 +59,40 @@ public class JsExecUtils {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 调用执行蓝奏云js文件
|
||||
*/
|
||||
public static Object executeOtherJs(String jsText, String funName, Object ... args) throws ScriptException,
|
||||
NoSuchMethodException {
|
||||
ScriptEngineManager engineManager = new ScriptEngineManager();
|
||||
ScriptEngine engine = engineManager.getEngineByName("JavaScript"); // 得到脚本引擎
|
||||
engine.eval(jsText);
|
||||
Invocable inv = (Invocable) engine;
|
||||
//调用js中的函数
|
||||
if (StringUtils.isNotEmpty(funName)) {
|
||||
return inv.invokeFunction(funName, args);
|
||||
}
|
||||
throw new ScriptException("funName is null");
|
||||
}
|
||||
|
||||
public static String getKwSign(String s, String pwd) {
|
||||
try {
|
||||
return executeOtherJs(JsContent.kwSignString, "encrypt", s, pwd).toString();
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
//encrypt("ZYcEEs2JdncXG8zAaytJiXxmbyhH2wxb", "Hm_Iuvt_cdb524f42f23cer9b268564v7y735ewrq2324")
|
||||
//'7909e8e754545a61ba4bc3c90c82cb6c69b6859d5ea2e46a6bf913d1b4f11dee011dced1'
|
||||
// 1e3170f1cc1ca75172409e443b89261ec777e190ebc595b458b8e114a912a9544d2b467323f8ca011b2ed0
|
||||
// 93a44ef48949d950c91303c84d36
|
||||
// 95dea502a45fb153f68d8da0bf8e4a095a001e396f60837e9c1b58a48969eb77038234d2
|
||||
// 93c3750f6ccf9d11b5c304b32495
|
||||
System.out.println(getKwSign("Hm_lvt_cdb524f42f0ce19b169a8071123a4797", "1729503755"));
|
||||
System.out.println(getKwSign("HelloWorld", "password123"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
40
parser/src/main/java/cn/qaiu/util/URLUtil.java
Normal file
40
parser/src/main/java/cn/qaiu/util/URLUtil.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package cn.qaiu.util;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class URLUtil {
|
||||
|
||||
private Map<String, String> queryParams = new HashMap<>();
|
||||
|
||||
// 构造函数,传入URL并解析参数
|
||||
private URLUtil(String url) {
|
||||
try {
|
||||
URL parsedUrl = new URL(url);
|
||||
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") : "";
|
||||
queryParams.put(key, value);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// 静态方法,用于创建UrlUtil实例
|
||||
public static URLUtil from(String url) {
|
||||
return new URLUtil(url);
|
||||
}
|
||||
|
||||
// 获取参数的方法
|
||||
public String getParam(String param) {
|
||||
return queryParams.get(param);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package qaiu.web.test;
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.Vertx;
|
||||
@@ -1,4 +1,4 @@
|
||||
package qaiu.web.test;
|
||||
package cn.qaiu.util;
|
||||
|
||||
import cn.qaiu.util.AESUtils;
|
||||
import org.junit.Assert;
|
||||
@@ -1,4 +1,4 @@
|
||||
package qaiu.web.test;
|
||||
package cn.qaiu.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
19
parser/src/test/java/cn/qaiu/util/URLUtilTest.java
Normal file
19
parser/src/test/java/cn/qaiu/util/URLUtilTest.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package cn.qaiu.util;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class URLUtilTest {
|
||||
|
||||
@Test
|
||||
public void getParam() {
|
||||
|
||||
URLUtil util = URLUtil.from("https://i.y.qq.com/v8/playsong.html?ADTAG=cbshare&_wv=1&appshare=android_qq" +
|
||||
"&appsongtype=1&appversion=13100008&channelId=10036163&hosteuin=7iosow-s7enz&openinqqmusic=1&platform" +
|
||||
"=11&songmid=000XjcLg0fbRjv&type=0");
|
||||
Assert.assertEquals(util.getParam("songmid"), "000XjcLg0fbRjv");
|
||||
Assert.assertEquals(util.getParam("type"), "0");
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package qaiu.web.test;
|
||||
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TestWebClient2 {
|
||||
|
||||
@Test
|
||||
public void matcherHtml() {
|
||||
|
||||
Pattern compile = Pattern.compile("class=\"ifr2\" name=.+src=\"(/fn\\?[a-zA-Z0-9_+/=]{16,})\"");
|
||||
var text = """
|
||||
<div class="ifr"><!--<iframe class="ifr2" name="1" src="/fn?v2" frameborder="0" scrolling="no"></iframe>-->
|
||||
<iframe class="ifr2" name="1685001208" src="/fn?UzUBa1oxBmUAYgNsUDUFNVI6BjJfJlchV21TZFU_aVWwANVQzXTBXMlUxUTcLZ1dwUn8DYwQ5AHFVOwdmBjRUPlM2AS9aOgY3AGIDMFA2" frameborder="0" scrolling="no"></iframe>""";
|
||||
System.out.println(text);
|
||||
Matcher matcher = compile.matcher(text);
|
||||
if (matcher.find()) {
|
||||
System.out.println(matcher.group(0));
|
||||
System.out.println(matcher.group(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lzClient() {
|
||||
Vertx vertx = Vertx.vertx();
|
||||
WebClient client = WebClient.create(vertx);
|
||||
MultiMap form = MultiMap.caseInsensitiveMultiMap();
|
||||
|
||||
|
||||
// action=downprocess&sign=AGYHOQk4BjdTWgQ7BzcGOlU_bATVSNQMxBDFQZgZoBj4HMFEgWnMOZ1I1A2NWOgUxB20HMlM_aUGoLOgQz&p=e4k4
|
||||
form.set("action", "downprocess");
|
||||
form.set("sign", "VzFWaA4_aBzYIAQI9ADBUaARvATVRNlNhUGUBNwBuATkDNFEgXHVVPAZhB2dTP1ZiVD5UYQBpV2EBPwA3");
|
||||
form.set("p", "e4k4");
|
||||
client.postAbs("https://wwsd.lanzoue.com/ajaxm.php")
|
||||
.putHeader("referer", "https://wwsd.lanzoue.com/iFhd00x8k0kh")
|
||||
.sendForm(form).onSuccess(res -> {
|
||||
JsonObject jsonObject = res.bodyAsJsonObject();
|
||||
System.out.println(jsonObject);
|
||||
|
||||
vertx.close();
|
||||
});
|
||||
|
||||
//
|
||||
// https://developer.lanzoug.com/file/?VTMBPwEwU2IGD1dvV2ICblBvU2sENQZhVSZSMAA1WihSOVYsDTZTZlN2UXMFfAZjBzJXJQAxAG5XP1UyXGQGKlVlAXgBbVMpBmNXLFdhAmpQZFN4BCEGbVUiUnIAOloyUj5WZA0PU25TYVE6BWAGNgdlV2IAbQAyV2JValw3BiFVMwElAWFTNgZmVzBXMwIyUDpTYARrBiJVIlIkAGFaaVJiVjMNY1MoUzVRMgV+BjUHaFd9ADwAMVdlVTFcOAYyVWcBYgFqUz4GaVdlVzMCNFBrUzcEOAZgVWJSZQA/WmJSM1Y2DWhTNFMzUTEFYgY3B2VXZgBxAHhXOVUjXCYGclUmATMBLlNuBjRXPFcyAjNQP1NvBG8GPVVqUnIAKFoyUj9WZA02UzpTNFExBWkGMgdtV2IAaQAxV2FVYFwuBilVcwEwATBTcAZtVzBXNQI7UD9TZgRrBjFVY1JlAGlafVInVnENJ1M6UzRRMQVpBjIHbVdiAG0AMldgVWRcJgZyVTwBJgFhUzYGYVczVy0CMVA5U2QEdQY1VWZSYgByWmxSag==
|
||||
// https://developer.lanzoug.com/file/?B2FWaA4/BDVTWgc/UWRVOQQ7BT1VZFYxUSJUNgE0UiAEbwJ4CDMOOwInU3EKc1w5ATRSIAIzUz1ROVcwATkDLwc3Vi8OYgR+UzYHfFFnVT0EMAUuVXBWPVEmVHQBO1I6BGgCMAgKDjMCMFM4Cm9cbAFjUmcCb1NhUWRXaAFqAyQHYVZyDm4EYVMzB2BRNVVlBG4FNlU6VnJRJlQiAWBSYQQ0AmcIZg51AmRTMApxXG8BblJ4Aj5TYlFjVzMBZQM3BzVWNQ5lBGlTPAc1UTVVYwQ/BWFVaVYwUWZUYwE+UmoEZQJiCG0OaQJiUzMKbVxtAWNSYwJzUytRP1chAXsDdwd0VmQOIQQ5U2EHbFE0VWQEawU5VT5WbVFuVHQBKVI6BGkCMAgzDmcCZVMzCmZcaAFrUmcCaFNlUWNXZwFzAywHIVZnDj8EJ1M4B2BRM1VsBGsFMFU6VmZRb1RgAW5SdQRxAiUIIg5nAmVTMwpmXGgBa1JnAm9TYVFmV2YBewN3B25WcQ5uBGFTNAdjUStVZgRtBTJVJFZlUWJUZAFzUmQEPA==
|
||||
// https://developer.lanzoug.com/file/?CW9WaAk4BzZUXVRsCz5cMAE+Bj5UZVM0USJUNlRhA3FUPwJ4CTJUYQInASMHflI3ATQGdFdmAW9ROQFmVGwEKAk5Vi8JZQd9VDFULws9XDQBNQYtVHFTOFEmVHRUbgNrVDgCMAkLVGkCMAFqB2JSYgFjBjNXOgEzUWQBPlQ/BCMJb1ZyCWkHYlQ0VDMLb1xsAWsGNVQ7U3dRJlQiVDUDMFRkAmcJZ1QvAmQBYgd8UmEBbgYsV2sBMFFjAWVUMAQwCTtWNQliB2pUO1RmC29cagE6BmJUaFM1UWZUY1RrAztUNQJiCWxUMwJiAWEHYFJjAWMGN1cmAXlRPwF3VC4EcAl6VmQJJgc6VGZUPwtuXG0BbgY6VD9TaFFuVHRUfANrVDkCMAkyVD0CZQFhB2tSZgFrBjNXPgE6UWABM1QmBCsJL1ZnCTgHJFQ/VDMLaVxlAW4GM1Q7U2RRb1RmVDgDJFQhAiUJI1Q9AmUBYQdrUmYBawYzVzoBM1FmATBULgRwCWBWcQlpB2JUM1QwC3FcbwFoBjFUJVNgUWJUZFQmAzVUbA==
|
||||
}
|
||||
}
|
||||
@@ -58,5 +58,7 @@ cache:
|
||||
ws:
|
||||
ye:
|
||||
mne: 30
|
||||
mqq: 30
|
||||
mkg: 30
|
||||
|
||||
|
||||
|
||||
45
web-service/src/main/resources/http-tools/pan-mkg.http
Normal file
45
web-service/src/main/resources/http-tools/pan-mkg.http
Normal file
@@ -0,0 +1,45 @@
|
||||
#@no-cookie-jar
|
||||
#@no-redirect
|
||||
https://t1.kugou.com/song.html?id=2bi8Fe9CSV3
|
||||
|
||||
|
||||
###
|
||||
GET https://www.kugou.com/share/2bi8Fe9CSV3.html?id=2bi8Fe9CSV3
|
||||
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
|
||||
if-modified-since: Mon, 21 Oct 2024 08:45:50 GMT
|
||||
priority: u=0, i
|
||||
sec-ch-ua: "Microsoft Edge";v="129", "Not=A?Brand";v="8", "Chromium";v="129"
|
||||
sec-ch-ua-mobile: ?0
|
||||
sec-ch-ua-platform: "Windows"
|
||||
sec-fetch-dest: document
|
||||
sec-fetch-mode: navigate
|
||||
sec-fetch-site: none
|
||||
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 Edg/129.0.0.0
|
||||
|
||||
###
|
||||
|
||||
|
||||
https://www.kuwo.cn/api/v1/www/music/playUrl?mid=395500809&type=music&httpsStatus=1&reqId=&plat=web_www&from=
|
||||
Cookie: Hm_Iuvt_cdb524f42f23cer9b268564v7y735ewrq2324=c7nkKBeXXzCyTQ8Wc8DRNYc4Th3f6hTE
|
||||
Secret: 4067e5c95a650d73865fe3e81feb897a6bf7b58579a3ff335de848ffb0ee31c9058333c0
|
||||
|
||||
###
|
||||
https://nmobi.kuwo.cn/mobi.s?f=kuwo&q=NI8S5evAnmGldi4g47EsqtfDbGsJckckbTQQd2LAgmDPITUWSd51OkjHRFj6xHPEQxNN6u+tD3K2e3HYhbE4U0pUYwqjd2kt
|
||||
|
||||
###
|
||||
https://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=395500809
|
||||
|
||||
###
|
||||
https://kuwo.cn/api/v1/www/music/playUrl?mid=395500809&type=music&httpsStatus=1&reqId=e09294a0-8f90-11ef-9b50-49e06d70bd68&plat=web_www&from=
|
||||
|
||||
###
|
||||
https://kuwo.cn/play_detail/395500809
|
||||
|
||||
###
|
||||
#@no-cookie-jar
|
||||
https://www.kuwo.cn/api/v1/www/music/playUrl?mid=395500809&type=music&httpsStatus=1&reqId=&plat=web_www&from=
|
||||
Cookie: Hm_Iuvt_cdb524f42f23cer9b268564v7y735ewrq2324=eZCHRdwSSYkG4d5XRGYtXrMpAXmGE5mh
|
||||
Secret: 460ac8ea43431f788d7ccbd67fde84755a88a8a36f88d17748d816dec3b308e402f768e8
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -199,8 +199,15 @@ GET http://127.0.0.1:6401/parser?url=https://474b.com/file/4015376-131945810
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6401/parser?url=http://163cn.tv/ykLZJJT
|
||||
|
||||
### PASS MQQ
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6401/parser?url=https://c6.y.qq.com/base/fcgi-bin/u?__=k8gafY6HAQ5Y
|
||||
|
||||
### PASS MKG
|
||||
# @no-redirect
|
||||
GET http://127.0.0.1:6401/parser?url=https://t1.kugou.com/song.html?id=2bi8Fe9CSV3
|
||||
|
||||
### n1
|
||||
http://127.0.0.1:6401/n1/statisticsInfo
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ proxy:
|
||||
# 1.origin代理地址端口后有目录(包括 / ),转发后地址:代理地址+访问URL目录部分去除location匹配目录
|
||||
# 2.origin代理地址端口后无任何,转发后地址:代理地址+访问URL目录部
|
||||
location:
|
||||
- path: ~^/(json/|v2/|parser|ye/|lz/|cow/|ec/|fj/|fc/|le/|qq/|ws/|iz/|ce|mne/).*
|
||||
- path: ~^/(json/|v2/|parser|ye/|lz/|cow/|ec/|fj/|fc/|le/|qq/|ws/|iz/|ce|mne|mqq|mkg/).*
|
||||
origin: 127.0.0.1:6400
|
||||
|
||||
# json/parser -> xxx/parser
|
||||
|
||||
Reference in New Issue
Block a user