修复蓝奏优享解析失败, gz压缩的json解析

This commit is contained in:
QAIU
2025-01-04 15:21:15 +08:00
parent 89713e6ac9
commit d91460d2e2
2 changed files with 119 additions and 42 deletions

View File

@@ -5,7 +5,7 @@ import cn.qaiu.entity.ShareLinkInfo;
import io.vertx.core.Future; import io.vertx.core.Future;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.Promise; import io.vertx.core.Promise;
import io.vertx.core.json.DecodeException; import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.core.net.ProxyOptions; import io.vertx.core.net.ProxyOptions;
import io.vertx.core.net.ProxyType; import io.vertx.core.net.ProxyType;
@@ -17,8 +17,14 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.zip.GZIPInputStream;
/** /**
* 解析器抽象类包含promise, HTTP Client, 默认失败方法等; * 解析器抽象类包含promise, HTTP Client, 默认失败方法等;
@@ -146,21 +152,56 @@ public abstract class PanBase implements IPanTool {
return handleFail(""); return handleFail("");
} }
/** /**
* bodyAsJsonObject的封装, 会自动处理异常 * bodyAsJsonObject的封装, 会自动处理异常
*
* @param res HttpResponse * @param res HttpResponse
* @return JsonObject * @return JsonObject
*/ */
protected JsonObject asJson(HttpResponse<?> res) { protected JsonObject asJson(HttpResponse<?> res) {
try { try {
return res.bodyAsJsonObject(); // 检查响应头中的Content-Encoding是否为gzip
} catch (DecodeException e) { String contentEncoding = res.getHeader("Content-Encoding");
if ("gzip".equalsIgnoreCase(contentEncoding)) {
// 如果是gzip压缩的响应体解压
return new JsonObject(decompressGzip((Buffer) res.body()));
} else {
return res.bodyAsJsonObject();
}
} catch (Exception e) {
fail("解析失败: json格式异常: {}", res.bodyAsString()); fail("解析失败: json格式异常: {}", res.bodyAsString());
throw new RuntimeException("解析失败: json格式异常"); throw new RuntimeException("解析失败: json格式异常");
} }
} }
/**
* 解压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) { protected void complete(String url) {
promise.complete(url); promise.complete(url);
} }

View File

@@ -15,6 +15,7 @@ import java.util.UUID;
* *
*/ */
public class IzTool extends PanBase { public class IzTool extends PanBase {
private static final String API_URL_PREFIX = "https://api.ilanzou.com/unproved/"; private static final String API_URL_PREFIX = "https://api.ilanzou.com/unproved/";
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" + private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" +
@@ -24,12 +25,38 @@ public class IzTool extends PanBase {
"&devType=6&uuid={uuid}&timestamp={ts}&auth={auth}&shareId={dataKey}"; "&devType=6&uuid={uuid}&timestamp={ts}&auth={auth}&shareId={dataKey}";
// downloadId=x&enable=1&devType=6&uuid=x&timestamp=x&auth=x&shareId=lGFndCM // downloadId=x&enable=1&devType=6&uuid=x&timestamp=x&auth=x&shareId=lGFndCM
private static final String VIP_REQUEST_URL = API_URL_PREFIX + "/buy/vip/list?devType=6&devModel=Chrome&uuid" +
"={uuid}&extra=2&timestamp={ts}";
private static final MultiMap header;
static {
header = MultiMap.caseInsensitiveMultiMap();
header.set("Accept", "application/json, text/plain, */*");
header.set("Accept-Encoding", "gzip, deflate, br, zstd");
header.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
header.set("Cache-Control", "no-cache");
header.set("Connection", "keep-alive");
header.set("Content-Length", "0");
header.set("DNT", "1");
header.set("Host", "api.ilanzou.com");
header.set("Origin", "https://www.ilanzou.com/");
header.set("Pragma", "no-cache");
header.set("Referer", "https://www.ilanzou.com/");
header.set("Sec-Fetch-Dest", "empty");
header.set("Sec-Fetch-Mode", "cors");
header.set("Sec-Fetch-Site", "cross-site");
header.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36");
header.set("sec-ch-ua", "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"");
header.set("sec-ch-ua-mobile", "?0");
header.set("sec-ch-ua-platform", "\"Windows\"");
}
public IzTool(ShareLinkInfo shareLinkInfo) { public IzTool(ShareLinkInfo shareLinkInfo) {
super(shareLinkInfo); super(shareLinkInfo);
} }
public Future<String> parse() { public Future<String> parse() {
String dataKey = shareLinkInfo.getShareKey(); String shareId = shareLinkInfo.getShareKey();
long nowTs = System.currentTimeMillis(); long nowTs = System.currentTimeMillis();
String tsEncode = AESUtils.encrypt2HexIz(Long.toString(nowTs)); String tsEncode = AESUtils.encrypt2HexIz(Long.toString(nowTs));
String uuid = UUID.randomUUID().toString(); String uuid = UUID.randomUUID().toString();
@@ -38,45 +65,54 @@ public class IzTool extends PanBase {
// String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey)); // String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey));
// 第一次请求 获取文件信息 // 第一次请求 获取文件信息
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60 // POST https://api.ilanzou.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL))
.setTemplateParam("shareId", dataKey) client.postAbs(UriTemplate.of(VIP_REQUEST_URL))
.setTemplateParam("uuid", uuid) .setTemplateParam("uuid", uuid)
.setTemplateParam("ts", tsEncode) .setTemplateParam("ts", tsEncode)
.send().onSuccess(res -> { .send().onSuccess(r0 -> { // 忽略res
JsonObject resJson = asJson(res); // 第一次请求 获取文件信息
if (resJson.getInteger("code") != 200) { // POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson); client.postAbs(UriTemplate.of(FIRST_REQUEST_URL))
return; .putHeaders(header)
} .setTemplateParam("shareId", shareId)
if (resJson.getJsonArray("list").size() == 0) { .setTemplateParam("uuid", uuid)
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson); .setTemplateParam("ts", tsEncode)
return; .send().onSuccess(res -> {
} JsonObject resJson = asJson(res);
// 文件Id if (resJson.getInteger("code") != 200) {
JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0); fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
String fileId = fileInfo.getString("fileIds"); return;
String userId = fileInfo.getString("userId"); }
// 其他参数 if (resJson.getJsonArray("list").size() == 0) {
// String fidEncode = AESUtils.encrypt2HexIz(fileId + "|"); fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId); return;
String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs); }
// 第二次请求 // 文件Id
clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL)) JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0);
.setTemplateParam("fidEncode", fidEncode) String fileId = fileInfo.getString("fileIds");
.setTemplateParam("uuid", uuid) String userId = fileInfo.getString("userId");
.setTemplateParam("ts", tsEncode) // 其他参数
.setTemplateParam("auth", auth) // String fidEncode = AESUtils.encrypt2HexIz(fileId + "|");
.setTemplateParam("shareId", dataKey).send().onSuccess(res2 -> { String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId);
MultiMap headers = res2.headers(); String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs);
if (!headers.contains("Location")) { // 第二次请求
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers()); clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
return; .setTemplateParam("fidEncode", fidEncode)
} .setTemplateParam("uuid", uuid)
promise.complete(headers.get("Location")); .setTemplateParam("ts", tsEncode)
}).onFailure(handleFail(SECOND_REQUEST_URL)); .setTemplateParam("auth", auth)
}).onFailure(handleFail(FIRST_REQUEST_URL)); .setTemplateParam("shareId", shareId)
.putHeaders(header).send().onSuccess(res2 -> {
MultiMap headers = res2.headers();
if (!headers.contains("Location")) {
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers());
return;
}
promise.complete(headers.get("Location"));
}).onFailure(handleFail(SECOND_REQUEST_URL));
}).onFailure(handleFail(FIRST_REQUEST_URL));
});
return promise.future(); return promise.future();
} }
} }