mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-17 04:43:02 +00:00
优化内核, QQ邮箱微信账户分享,添加123请求header
This commit is contained in:
@@ -175,8 +175,13 @@ public class RouterHandlerFactory implements BaseHttpApi {
|
|||||||
route.handler(ResponseTimeHandler.create());
|
route.handler(ResponseTimeHandler.create());
|
||||||
route.handler(ctx -> handlerMethod(instance, method, ctx)).failureHandler(ctx -> {
|
route.handler(ctx -> handlerMethod(instance, method, ctx)).failureHandler(ctx -> {
|
||||||
if (ctx.response().ended()) return;
|
if (ctx.response().ended()) return;
|
||||||
ctx.failure().printStackTrace();
|
// 超时处理器状态码503
|
||||||
doFireJsonResultResponse(ctx, JsonResult.error(ctx.failure().getMessage(), 500));
|
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)) {
|
} else if (method.isAnnotationPresent(SockRouteMapper.class)) {
|
||||||
// websocket 基于sockJs
|
// websocket 基于sockJs
|
||||||
|
|||||||
@@ -26,10 +26,20 @@ public class ResponseUtil {
|
|||||||
.end(jsonObject.encode());
|
.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 <T> void fireJsonResultResponse(RoutingContext ctx, JsonResult<T> jsonResult) {
|
public static <T> void fireJsonResultResponse(RoutingContext ctx, JsonResult<T> jsonResult) {
|
||||||
fireJsonObjectResponse(ctx, jsonResult.toJsonObject());
|
fireJsonObjectResponse(ctx, jsonResult.toJsonObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> void fireJsonResultResponse(HttpServerResponse ctx, JsonResult<T> jsonResult) {
|
||||||
|
fireJsonObjectResponse(ctx, jsonResult.toJsonObject());
|
||||||
|
}
|
||||||
|
|
||||||
public static void fireTextResponse(RoutingContext ctx, String text) {
|
public static void fireTextResponse(RoutingContext ctx, String text) {
|
||||||
ctx.response().putHeader(CONTENT_TYPE, "text/html; charset=utf-8").end(text);
|
ctx.response().putHeader(CONTENT_TYPE, "text/html; charset=utf-8").end(text);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ public class FileInfo {
|
|||||||
Long size;
|
Long size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MIME类型
|
* 类型
|
||||||
*/
|
*/
|
||||||
String fileMIME;
|
String fileType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件路径
|
* 文件路径
|
||||||
@@ -34,6 +34,11 @@ public class FileInfo {
|
|||||||
*/
|
*/
|
||||||
String createTime;
|
String createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上次修改时间
|
||||||
|
*/
|
||||||
|
String updateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建者
|
* 创建者
|
||||||
*/
|
*/
|
||||||
@@ -81,12 +86,12 @@ public class FileInfo {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFileMIME() {
|
public String getFileType() {
|
||||||
return fileMIME;
|
return fileType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileInfo setFileMIME(String fileMIME) {
|
public FileInfo setFileType(String fileType) {
|
||||||
this.fileMIME = fileMIME;
|
this.fileType = fileType;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +113,15 @@ public class FileInfo {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUpdateTime() {
|
||||||
|
return updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileInfo setUpdateTime(String updateTime) {
|
||||||
|
this.updateTime = updateTime;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String getCreateBy() {
|
public String getCreateBy() {
|
||||||
return createBy;
|
return createBy;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,13 @@ public class ShareLinkInfo {
|
|||||||
private String shareUrl; // 原始分享链接
|
private String shareUrl; // 原始分享链接
|
||||||
private String standardUrl; // 规范化的标准链接
|
private String standardUrl; // 规范化的标准链接
|
||||||
|
|
||||||
private Map<String, Object> otherParam; // 其他参数
|
/**
|
||||||
|
* 其他参数预定义
|
||||||
|
* auths: 认证相关 传入
|
||||||
|
* UA: 浏览器请求头 传入
|
||||||
|
* fileInfo: 解析成功的文件信息对象 传出
|
||||||
|
*/
|
||||||
|
private Map<String, Object> otherParam;
|
||||||
|
|
||||||
private ShareLinkInfo(Builder builder) {
|
private ShareLinkInfo(Builder builder) {
|
||||||
this.shareKey = builder.shareKey;
|
this.shareKey = builder.shareKey;
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ public abstract class PanBase implements IPanTool {
|
|||||||
return handleFail("");
|
return handleFail("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bodyAsJsonObject的封装, 会自动处理异常
|
* 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) {
|
protected void complete(String url) {
|
||||||
promise.complete(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,12 @@ public enum PanDomainTemplate {
|
|||||||
"https://iwx.mail.qq.com/ftn/download/{shareKey}",
|
"https://iwx.mail.qq.com/ftn/download/{shareKey}",
|
||||||
"https://mail.qq.com",
|
"https://mail.qq.com",
|
||||||
QQTool.class),
|
QQTool.class),
|
||||||
|
// https://wx.mail.qq.com/s?k=uAG9JR42Rqgt010mFp
|
||||||
|
QQW("QQ邮箱中转站(微信账户)",
|
||||||
|
compile("https://wx\\.mail\\.qq\\.com/s\\?k=(?<KEY>.+)"),
|
||||||
|
"https://wx.mail.qq.com/s?k={shareKey}",
|
||||||
|
"https://mail.qq.com",
|
||||||
|
QQwTool.class),
|
||||||
// https://f.ws59.cn/f/或者https://www.wenshushu.cn/f/
|
// https://f.ws59.cn/f/或者https://www.wenshushu.cn/f/
|
||||||
WS("文叔叔",
|
WS("文叔叔",
|
||||||
compile("https://(f\\.ws(\\d{2})\\.cn|www\\.wenshushu\\.cn)/f/(?<KEY>.+)"),
|
compile("https://(f\\.ws(\\d{2})\\.cn|www\\.wenshushu\\.cn)/f/(?<KEY>.+)"),
|
||||||
@@ -96,6 +102,7 @@ public enum PanDomainTemplate {
|
|||||||
"https://www.vyuyun.com/s/{shareKey}?password={pwd}",
|
"https://www.vyuyun.com/s/{shareKey}?password={pwd}",
|
||||||
PvyyTool.class),
|
PvyyTool.class),
|
||||||
// https://1drv.ms/w/s!Alg0feQmCv2rnRFd60DQOmMa-Oh_?e=buaRtp
|
// https://1drv.ms/w/s!Alg0feQmCv2rnRFd60DQOmMa-Oh_?e=buaRtp
|
||||||
|
// https://1drv.ms/u/c/abfd0a26e47d3458/EdYACWvPq85Et797YmvL5LgBruUKoNxqIFATXhIv1PI2_Q?e=z4ffNJ
|
||||||
POD("OneDrive",
|
POD("OneDrive",
|
||||||
compile("https://1drv\\.ms/(?<KEY>.+)"),
|
compile("https://1drv\\.ms/(?<KEY>.+)"),
|
||||||
"https://1drv\\.ms/{shareKey}",
|
"https://1drv\\.ms/{shareKey}",
|
||||||
@@ -117,7 +124,7 @@ public enum PanDomainTemplate {
|
|||||||
"https://www.dropbox.com/scl/fi/{shareKey}/?rlkey={pwd}&dl=0",
|
"https://www.dropbox.com/scl/fi/{shareKey}/?rlkey={pwd}&dl=0",
|
||||||
PdbTool.class),
|
PdbTool.class),
|
||||||
P115("115网盘",
|
P115("115网盘",
|
||||||
compile("https://(115|anxia).com/s/(?<KEY>\\w+)(\\?password=(?<PWD>\\w+))?(&.+)?"),
|
compile("https://(115|anxia).com/s/(?<KEY>\\w+)(\\?password=(?<PWD>\\w+))?([&#].*)?"),
|
||||||
"https://115.com/s/{shareKey}?password={pwd}",
|
"https://115.com/s/{shareKey}?password={pwd}",
|
||||||
P115Tool.class),
|
P115Tool.class),
|
||||||
// =====================音乐类解析 分享链接标志->MxxS (单歌曲/普通音质)==========================
|
// =====================音乐类解析 分享链接标志->MxxS (单歌曲/普通音质)==========================
|
||||||
|
|||||||
56
parser/src/main/java/cn/qaiu/parser/impl/QQwTool.java
Normal file
56
parser/src/main/java/cn/qaiu/parser/impl/QQwTool.java
Normal file
@@ -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<String> 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<String, String> extractVariables(String jsCode) {
|
||||||
|
Map<String, String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import cn.qaiu.parser.PanBase;
|
|||||||
import cn.qaiu.util.CommonUtils;
|
import cn.qaiu.util.CommonUtils;
|
||||||
import cn.qaiu.util.JsExecUtils;
|
import cn.qaiu.util.JsExecUtils;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
|
import io.vertx.core.MultiMap;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
import io.vertx.ext.web.client.WebClient;
|
import io.vertx.ext.web.client.WebClient;
|
||||||
import io.vertx.uritemplate.UriTemplate;
|
import io.vertx.uritemplate.UriTemplate;
|
||||||
@@ -17,6 +18,8 @@ import java.util.Map;
|
|||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static cn.qaiu.util.RandomStringGenerator.gen36String;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 123网盘
|
* 123网盘
|
||||||
*/
|
*/
|
||||||
@@ -30,8 +33,27 @@ public class YeTool extends PanBase {
|
|||||||
"&shareKey={shareKey}&SharePwd={pwd}&ParentFileId=0&Page=1&event=homeListFile&operateType=1";
|
"&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 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) {
|
public YeTool(ShareLinkInfo shareLinkInfo) {
|
||||||
super(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<String> parse() {
|
public Future<String> parse() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cn.qaiu.util;
|
package cn.qaiu.util;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class RandomStringGenerator {
|
public class RandomStringGenerator {
|
||||||
private static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789";
|
private static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
@@ -19,4 +20,10 @@ public class RandomStringGenerator {
|
|||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String gen36String() {
|
||||||
|
String uuid = UUID.randomUUID().toString().toLowerCase();
|
||||||
|
// 移除短横线
|
||||||
|
return uuid.replace("-", "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ public class CacheConfigLoader {
|
|||||||
TYPE = config.getString("type");
|
TYPE = config.getString("type");
|
||||||
Integer defaultDuration = config.getInteger("defaultDuration");
|
Integer defaultDuration = config.getInteger("defaultDuration");
|
||||||
DEFAULT_DURATION = defaultDuration == null ? 60 : 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) {
|
if (v == null) {
|
||||||
CONFIGS.put(k, DEFAULT_DURATION);
|
CONFIGS.put(k, DEFAULT_DURATION);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ public class ParserApi {
|
|||||||
return Arrays.stream(PanDomainTemplate.values()).map(pan -> new TreeMap<String, String>() {{
|
return Arrays.stream(PanDomainTemplate.values()).map(pan -> new TreeMap<String, String>() {{
|
||||||
put("name", pan.getDisplayName());
|
put("name", pan.getDisplayName());
|
||||||
put("type", pan.name().toLowerCase());
|
put("type", pan.name().toLowerCase());
|
||||||
|
put("shareUrlFormat", pan.getStandardUrlTemplate());
|
||||||
put("url", pan.getPanDomain());
|
put("url", pan.getPanDomain());
|
||||||
}}).collect(Collectors.toList());
|
}}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package cn.qaiu.lz.web.model;
|
|||||||
import cn.qaiu.db.ddl.Length;
|
import cn.qaiu.db.ddl.Length;
|
||||||
import cn.qaiu.db.ddl.Table;
|
import cn.qaiu.db.ddl.Table;
|
||||||
import cn.qaiu.db.ddl.TableGenIgnore;
|
import cn.qaiu.db.ddl.TableGenIgnore;
|
||||||
|
import cn.qaiu.entity.FileInfo;
|
||||||
import cn.qaiu.lz.common.ToJson;
|
import cn.qaiu.lz.common.ToJson;
|
||||||
import io.vertx.codegen.annotations.DataObject;
|
import io.vertx.codegen.annotations.DataObject;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
@@ -48,6 +49,8 @@ public class CacheLinkInfo implements ToJson {
|
|||||||
*/
|
*/
|
||||||
private Long expiration;
|
private Long expiration;
|
||||||
|
|
||||||
|
private FileInfo fileInfo;
|
||||||
|
|
||||||
|
|
||||||
// 使用 JsonObject 构造
|
// 使用 JsonObject 构造
|
||||||
public CacheLinkInfo(JsonObject json) {
|
public CacheLinkInfo(JsonObject json) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public class LinkInfoResp {
|
|||||||
// 解析链接
|
// 解析链接
|
||||||
private String downLink;
|
private String downLink;
|
||||||
private String apiLink;
|
private String apiLink;
|
||||||
|
private String viewLink;
|
||||||
private Integer cacheHitTotal;
|
private Integer cacheHitTotal;
|
||||||
private Integer parserTotal;
|
private Integer parserTotal;
|
||||||
private Integer sumTotal;
|
private Integer sumTotal;
|
||||||
|
|||||||
Reference in New Issue
Block a user