Skip to content

Commit d91460d

Browse files
committed
修复蓝奏优享解析失败, gz压缩的json解析
1 parent 89713e6 commit d91460d

File tree

2 files changed

+119
-42
lines changed

2 files changed

+119
-42
lines changed

parser/src/main/java/cn/qaiu/parser/PanBase.java

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import io.vertx.core.Future;
66
import io.vertx.core.Handler;
77
import io.vertx.core.Promise;
8-
import io.vertx.core.json.DecodeException;
8+
import io.vertx.core.buffer.Buffer;
99
import io.vertx.core.json.JsonObject;
1010
import io.vertx.core.net.ProxyOptions;
1111
import io.vertx.core.net.ProxyType;
@@ -17,8 +17,14 @@
1717
import org.slf4j.Logger;
1818
import org.slf4j.LoggerFactory;
1919

20+
import java.io.BufferedReader;
21+
import java.io.ByteArrayInputStream;
22+
import java.io.IOException;
23+
import java.io.InputStreamReader;
24+
import java.nio.charset.StandardCharsets;
2025
import java.util.Arrays;
2126
import java.util.Iterator;
27+
import java.util.zip.GZIPInputStream;
2228

2329
/**
2430
* 解析器抽象类包含promise, HTTP Client, 默认失败方法等;
@@ -146,21 +152,56 @@ protected Handler<Throwable> handleFail() {
146152
return handleFail("");
147153
}
148154

149-
150155
/**
151156
* bodyAsJsonObject的封装, 会自动处理异常
157+
*
152158
* @param res HttpResponse
153159
* @return JsonObject
154160
*/
155161
protected JsonObject asJson(HttpResponse<?> res) {
156162
try {
157-
return res.bodyAsJsonObject();
158-
} catch (DecodeException e) {
163+
// 检查响应头中的Content-Encoding是否为gzip
164+
String contentEncoding = res.getHeader("Content-Encoding");
165+
if ("gzip".equalsIgnoreCase(contentEncoding)) {
166+
// 如果是gzip压缩的响应体,解压
167+
return new JsonObject(decompressGzip((Buffer) res.body()));
168+
} else {
169+
return res.bodyAsJsonObject();
170+
}
171+
172+
} catch (Exception e) {
159173
fail("解析失败: json格式异常: {}", res.bodyAsString());
160174
throw new RuntimeException("解析失败: json格式异常");
161175
}
162176
}
163177

178+
/**
179+
* 解压gzip数据
180+
* @param compressedData compressedData
181+
* @return String
182+
* @throws IOException IOException
183+
*/
184+
private String decompressGzip(Buffer compressedData) throws IOException {
185+
try (ByteArrayInputStream bais = new ByteArrayInputStream(compressedData.getBytes());
186+
GZIPInputStream gzis = new GZIPInputStream(bais);
187+
BufferedReader reader = new BufferedReader(new InputStreamReader(gzis,
188+
StandardCharsets.UTF_8))) {
189+
190+
// 用于存储解压后的字符串
191+
StringBuilder decompressedData = new StringBuilder();
192+
193+
// 逐行读取解压后的数据
194+
String line;
195+
while ((line = reader.readLine()) != null) {
196+
decompressedData.append(line);
197+
}
198+
199+
// 此时decompressedData.toString()包含了解压后的字符串
200+
return decompressedData.toString();
201+
}
202+
203+
}
204+
164205
protected void complete(String url) {
165206
promise.complete(url);
166207
}

parser/src/main/java/cn/qaiu/parser/impl/IzTool.java

Lines changed: 74 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*
1616
*/
1717
public class IzTool extends PanBase {
18+
1819
private static final String API_URL_PREFIX = "https://api.ilanzou.com/unproved/";
1920

2021
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 {
2425
"&devType=6&uuid={uuid}&timestamp={ts}&auth={auth}&shareId={dataKey}";
2526
// downloadId=x&enable=1&devType=6&uuid=x&timestamp=x&auth=x&shareId=lGFndCM
2627

28+
private static final String VIP_REQUEST_URL = API_URL_PREFIX + "/buy/vip/list?devType=6&devModel=Chrome&uuid" +
29+
"={uuid}&extra=2&timestamp={ts}";
30+
private static final MultiMap header;
31+
32+
static {
33+
header = MultiMap.caseInsensitiveMultiMap();
34+
header.set("Accept", "application/json, text/plain, */*");
35+
header.set("Accept-Encoding", "gzip, deflate, br, zstd");
36+
header.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
37+
header.set("Cache-Control", "no-cache");
38+
header.set("Connection", "keep-alive");
39+
header.set("Content-Length", "0");
40+
header.set("DNT", "1");
41+
header.set("Host", "api.ilanzou.com");
42+
header.set("Origin", "https://www.ilanzou.com/");
43+
header.set("Pragma", "no-cache");
44+
header.set("Referer", "https://www.ilanzou.com/");
45+
header.set("Sec-Fetch-Dest", "empty");
46+
header.set("Sec-Fetch-Mode", "cors");
47+
header.set("Sec-Fetch-Site", "cross-site");
48+
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");
49+
header.set("sec-ch-ua", "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"");
50+
header.set("sec-ch-ua-mobile", "?0");
51+
header.set("sec-ch-ua-platform", "\"Windows\"");
52+
}
53+
2754
public IzTool(ShareLinkInfo shareLinkInfo) {
2855
super(shareLinkInfo);
2956
}
3057

3158
public Future<String> parse() {
32-
String dataKey = shareLinkInfo.getShareKey();
59+
String shareId = shareLinkInfo.getShareKey();
3360
long nowTs = System.currentTimeMillis();
3461
String tsEncode = AESUtils.encrypt2HexIz(Long.toString(nowTs));
3562
String uuid = UUID.randomUUID().toString();
@@ -38,45 +65,54 @@ public Future<String> parse() {
3865
// String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey));
3966

4067
// 第一次请求 获取文件信息
41-
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
42-
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL))
43-
.setTemplateParam("shareId", dataKey)
68+
// POST https://api.ilanzou.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
69+
70+
client.postAbs(UriTemplate.of(VIP_REQUEST_URL))
4471
.setTemplateParam("uuid", uuid)
4572
.setTemplateParam("ts", tsEncode)
46-
.send().onSuccess(res -> {
47-
JsonObject resJson = asJson(res);
48-
if (resJson.getInteger("code") != 200) {
49-
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
50-
return;
51-
}
52-
if (resJson.getJsonArray("list").size() == 0) {
53-
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
54-
return;
55-
}
56-
// 文件Id
57-
JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0);
58-
String fileId = fileInfo.getString("fileIds");
59-
String userId = fileInfo.getString("userId");
60-
// 其他参数
61-
// String fidEncode = AESUtils.encrypt2HexIz(fileId + "|");
62-
String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId);
63-
String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs);
64-
// 第二次请求
65-
clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
66-
.setTemplateParam("fidEncode", fidEncode)
67-
.setTemplateParam("uuid", uuid)
68-
.setTemplateParam("ts", tsEncode)
69-
.setTemplateParam("auth", auth)
70-
.setTemplateParam("shareId", dataKey).send().onSuccess(res2 -> {
71-
MultiMap headers = res2.headers();
72-
if (!headers.contains("Location")) {
73-
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers());
74-
return;
75-
}
76-
promise.complete(headers.get("Location"));
77-
}).onFailure(handleFail(SECOND_REQUEST_URL));
78-
}).onFailure(handleFail(FIRST_REQUEST_URL));
79-
73+
.send().onSuccess(r0 -> { // 忽略res
74+
// 第一次请求 获取文件信息
75+
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
76+
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL))
77+
.putHeaders(header)
78+
.setTemplateParam("shareId", shareId)
79+
.setTemplateParam("uuid", uuid)
80+
.setTemplateParam("ts", tsEncode)
81+
.send().onSuccess(res -> {
82+
JsonObject resJson = asJson(res);
83+
if (resJson.getInteger("code") != 200) {
84+
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
85+
return;
86+
}
87+
if (resJson.getJsonArray("list").size() == 0) {
88+
fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
89+
return;
90+
}
91+
// 文件Id
92+
JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0);
93+
String fileId = fileInfo.getString("fileIds");
94+
String userId = fileInfo.getString("userId");
95+
// 其他参数
96+
// String fidEncode = AESUtils.encrypt2HexIz(fileId + "|");
97+
String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId);
98+
String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs);
99+
// 第二次请求
100+
clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
101+
.setTemplateParam("fidEncode", fidEncode)
102+
.setTemplateParam("uuid", uuid)
103+
.setTemplateParam("ts", tsEncode)
104+
.setTemplateParam("auth", auth)
105+
.setTemplateParam("shareId", shareId)
106+
.putHeaders(header).send().onSuccess(res2 -> {
107+
MultiMap headers = res2.headers();
108+
if (!headers.contains("Location")) {
109+
fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers());
110+
return;
111+
}
112+
promise.complete(headers.get("Location"));
113+
}).onFailure(handleFail(SECOND_REQUEST_URL));
114+
}).onFailure(handleFail(FIRST_REQUEST_URL));
115+
});
80116
return promise.future();
81117
}
82118
}

0 commit comments

Comments
 (0)