diff --git a/app/src/main/java/com/github/catvod/api/AliYun.java b/app/src/main/java/com/github/catvod/api/AliYun.java index da1c9867..c0664bef 100644 --- a/app/src/main/java/com/github/catvod/api/AliYun.java +++ b/app/src/main/java/com/github/catvod/api/AliYun.java @@ -19,9 +19,13 @@ import com.github.catvod.bean.Sub; import com.github.catvod.bean.Vod; import com.github.catvod.bean.ali.Code; import com.github.catvod.bean.ali.Data; +import com.github.catvod.bean.ali.Download; import com.github.catvod.bean.ali.Drive; import com.github.catvod.bean.ali.Item; import com.github.catvod.bean.ali.OAuth; +import com.github.catvod.bean.ali.Preview; +import com.github.catvod.bean.ali.Res; +import com.github.catvod.bean.ali.Share; import com.github.catvod.bean.ali.User; import com.github.catvod.crawler.SpiderDebug; import com.github.catvod.net.OkHttp; @@ -31,9 +35,7 @@ import com.github.catvod.spider.Proxy; import com.github.catvod.utils.FileUtil; import com.github.catvod.utils.QRCode; import com.github.catvod.utils.Utils; - -import org.json.JSONArray; -import org.json.JSONObject; +import com.google.gson.JsonObject; import java.io.ByteArrayInputStream; import java.io.File; @@ -58,10 +60,9 @@ public class AliYun { private String refreshToken; private String shareToken; private String shareId; - private String driveId; private OAuth oauth; - private User user; private Drive drive; + private User user; private static class Loader { static volatile AliYun INSTANCE = new AliYun(); @@ -85,8 +86,8 @@ public class AliYun { private AliYun() { tempIds = new ArrayList<>(); - oauth = OAuth.objectFrom(FileUtil.read(getOAuthCache())); user = User.objectFrom(FileUtil.read(getUserCache())); + oauth = OAuth.objectFrom(FileUtil.read(getOAuthCache())); drive = Drive.objectFrom(FileUtil.read(getDriveCache())); } @@ -103,9 +104,10 @@ public class AliYun { } public void setShareId(String shareId) { - if (!getOAuthCache().exists()) oauth.clean().save(); if (!getUserCache().exists()) user.clean().save(); + if (!getOAuthCache().exists()) oauth.clean().save(); if (!getDriveCache().exists()) drive.clean().save(); + if (shareId.equals(this.shareId)) return; this.shareId = shareId; refreshShareToken(); } @@ -131,21 +133,18 @@ public class AliYun { return headers; } - private boolean alist(String url, JSONObject body) { - //https://api-cf.nn.ci/alist/ali_open/ - //https://api.xhofe.top/alist/ali_open/ - //https://sni_api_nn_ci.cooluc.com/alist/ali_open/ + private boolean alist(String url, JsonObject param) { String api = "https://aliapi.ewwe.gq/alist/ali_open/" + url; - OkResult result = OkHttp.postJson(api, body.toString(), getHeader()); + OkResult result = OkHttp.postJson(api, param.toString(), getHeader()); SpiderDebug.log(result.getCode() + "," + api + "," + result.getBody()); if (isManyRequest(result.getBody())) return false; oauth = OAuth.objectFrom(result.getBody()).save(); return true; } - private String post(String url, JSONObject body) { + private String post(String url, JsonObject param) { url = url.startsWith("https") ? url : "https://api.aliyundrive.com/" + url; - OkResult result = OkHttp.postJson(url, body.toString(), getHeader()); + OkResult result = OkHttp.postJson(url, param.toString(), getHeader()); SpiderDebug.log(result.getCode() + "," + url + "," + result.getBody()); return result.getBody(); } @@ -180,31 +179,27 @@ public class AliYun { } private void refreshShareToken() { - try { - SpiderDebug.log("refreshShareToken..."); - JSONObject body = new JSONObject(); - body.put("share_id", shareId); - body.put("share_pwd", ""); - String result = post("v2/share_link/get_share_token", body); - shareToken = new JSONObject(result).getString("share_token"); - } catch (Exception e) { - e.printStackTrace(); - Utils.notify("來晚啦,該分享已失效。"); - } + SpiderDebug.log("refreshShareToken..."); + JsonObject param = new JsonObject(); + param.addProperty("share_id", shareId); + param.addProperty("share_pwd", ""); + String json = post("v2/share_link/get_share_token", param); + shareToken = Share.objectFrom(json).getShareToken(); + if (shareToken.isEmpty()) Utils.notify("來晚啦,該分享已失效。"); } private boolean refreshAccessToken() { try { SpiderDebug.log("refreshAccessToken..."); - JSONObject body = new JSONObject(); + JsonObject param = new JsonObject(); String token = user.getRefreshToken(); if (token.isEmpty()) token = refreshToken; - if (token.startsWith("http")) token = OkHttp.string(token).trim(); - body.put("refresh_token", token); - body.put("grant_type", "refresh_token"); - String result = post("https://auth.aliyundrive.com/v2/account/token", body); - user = User.objectFrom(result).save(); - if (user.getAccessToken().isEmpty()) throw new Exception(result); + if (token != null && token.startsWith("http")) token = OkHttp.string(token).trim(); + param.addProperty("refresh_token", token); + param.addProperty("grant_type", "refresh_token"); + String json = post("https://auth.aliyundrive.com/v2/account/token", param); + user = User.objectFrom(json).save(); + if (user.getAccessToken().isEmpty()) throw new Exception(json); return true; } catch (Exception e) { if (e instanceof TimeoutException) return onTimeout(); @@ -218,103 +213,77 @@ public class AliYun { } } - private boolean getDriveId() { - try { - SpiderDebug.log("Obtain drive id..."); - String result = auth("https://user.aliyundrive.com/v2/user/get", "{}", false); - drive = Drive.objectFrom(result).save(); - driveId = drive.getResourceDriveId().isEmpty() ? drive.getDriveId() : drive.getResourceDriveId(); - return false; - } catch (Exception e) { - e.printStackTrace(); - drive.clean().save(); - return true; - } + private void getDriveId() { + SpiderDebug.log("Get Drive Id..."); + String json = auth("https://user.aliyundrive.com/v2/user/get", "{}", true); + drive = Drive.objectFrom(json).save(); } private boolean oauthRequest() { - try { - SpiderDebug.log("OAuth Request..."); - JSONObject body = new JSONObject(); - body.put("authorize", 1); - body.put("scope", "user:base,file:all:read,file:all:write"); - String url = "https://open.aliyundrive.com/oauth/users/authorize?client_id=" + BuildConfig.CLIENT_ID + "&redirect_uri=https://alist.nn.ci/tool/aliyundrive/callback&scope=user:base,file:all:read,file:all:write&state="; - String result = auth(url, body.toString(), true); - return oauthRedirect(Code.objectFrom(result).getCode()); - } catch (Exception e) { - e.printStackTrace(); - return false; - } + SpiderDebug.log("OAuth Request..."); + JsonObject param = new JsonObject(); + param.addProperty("authorize", 1); + param.addProperty("scope", "user:base,file:all:read,file:all:write"); + String url = "https://open.aliyundrive.com/oauth/users/authorize?client_id=" + BuildConfig.CLIENT_ID + "&redirect_uri=https://alist.nn.ci/tool/aliyundrive/callback&scope=user:base,file:all:read,file:all:write&state="; + String json = auth(url, param.toString(), true); + return oauthRedirect(Code.objectFrom(json).getCode()); } private boolean oauthRedirect(String code) { - try { - SpiderDebug.log("OAuth Redirect..."); - JSONObject body = new JSONObject(); - body.put("code", code); - body.put("grant_type", "authorization_code"); - return alist("code", body); - } catch (Exception e) { - e.printStackTrace(); - oauth.clean().save(); - return false; - } + SpiderDebug.log("OAuth Redirect..."); + JsonObject param = new JsonObject(); + param.addProperty("code", code); + param.addProperty("grant_type", "authorization_code"); + return alist("code", param); } private boolean refreshOpenToken() { - try { - if (oauth.getRefreshToken().isEmpty()) return oauthRequest(); - SpiderDebug.log("refreshOpenToken..."); - JSONObject body = new JSONObject(); - body.put("grant_type", "refresh_token"); - body.put("refresh_token", oauth.getRefreshToken()); - return alist("token", body); - } catch (Exception e) { - e.printStackTrace(); - oauth.clean().save(); - return false; - } + if (oauth.getRefreshToken().isEmpty()) return oauthRequest(); + SpiderDebug.log("refreshOpenToken..."); + JsonObject param = new JsonObject(); + param.addProperty("grant_type", "refresh_token"); + param.addProperty("refresh_token", oauth.getRefreshToken()); + return alist("token", param); } - public Vod getVod(String url, String fileId) throws Exception { - JSONObject body = new JSONObject(); - body.put("share_id", shareId); - String result = post("adrive/v3/share_link/get_share_by_anonymous", body); - JSONObject object = new JSONObject(result); + public Vod getVod(String url, String fileId) { + JsonObject param = new JsonObject(); + param.addProperty("share_id", shareId); + Share share = Share.objectFrom(post("adrive/v3/share_link/get_share_by_anonymous", param)); List files = new ArrayList<>(); List subs = new ArrayList<>(); - listFiles(new Item(getParentFileId(fileId, object)), files, subs); + listFiles(new Item(getParentFileId(fileId, share)), files, subs); Collections.sort(files); List playFrom = Arrays.asList("原畫", "普畫"); List episode = new ArrayList<>(); List playUrl = new ArrayList<>(); - for (Item file : files) episode.add(file.getDisplayName() + "$" + file.getFileId() + findSubs(file.getName(), subs)); + for (Item file : files) episode.add(file.getDisplayName() + "$" + shareId + "@" + file.getFileId() + findSubs(file.getName(), subs)); for (int i = 0; i < playFrom.size(); i++) playUrl.add(TextUtils.join("#", episode)); Vod vod = new Vod(); vod.setVodId(url); vod.setVodContent(url); - vod.setVodPic(object.getString("avatar")); - vod.setVodName(object.getString("share_name")); + vod.setVodPic(share.getAvatar()); + vod.setVodName(share.getShareName()); vod.setVodPlayUrl(TextUtils.join("$$$", playUrl)); vod.setVodPlayFrom(TextUtils.join("$$$", playFrom)); vod.setTypeName("阿里雲盤"); return vod; } - private void listFiles(Item folder, List files, List subs) throws Exception { + private void listFiles(Item folder, List files, List subs) { listFiles(folder, files, subs, ""); } - private void listFiles(Item parent, List files, List subs, String marker) throws Exception { - JSONObject body = new JSONObject(); + private void listFiles(Item parent, List files, List subs, String marker) { List folders = new ArrayList<>(); - body.put("limit", 200); - body.put("share_id", shareId); - body.put("parent_file_id", parent.getFileId()); - body.put("order_by", "name"); - body.put("order_direction", "ASC"); - if (marker.length() > 0) body.put("marker", marker); - Item item = Item.objectFrom(auth("adrive/v3/file/list", body.toString(), true)); + JsonObject param = new JsonObject(); + param.addProperty("limit", 200); + param.addProperty("share_id", shareId); + param.addProperty("parent_file_id", parent.getFileId()); + param.addProperty("order_by", "name"); + param.addProperty("order_direction", "ASC"); + if (marker.length() > 0) param.addProperty("marker", marker); + Item item = Item.objectFrom(auth("adrive/v3/file/list", param.toString(), true)); for (Item file : item.getItems()) { if (file.getType().equals("folder")) { folders.add(file); @@ -332,14 +301,11 @@ public class AliYun { } } - private String getParentFileId(String fileId, JSONObject shareInfo) throws Exception { - JSONArray array = shareInfo.getJSONArray("file_infos"); + private String getParentFileId(String fileId, Share share) { if (!TextUtils.isEmpty(fileId)) return fileId; - if (array.length() == 0) return ""; - JSONObject fileInfo = array.getJSONObject(0); - if (fileInfo.getString("type").equals("folder")) return fileInfo.getString("file_id"); - if (fileInfo.getString("type").equals("file") && fileInfo.getString("category").equals("video")) return "root"; - return ""; + if (share.getFileInfos().isEmpty()) return ""; + Item item = share.getFileInfos().get(0); + return item.getType().equals("folder") ? item.getFileId() : "root"; } private void pair(String name1, List items, List subs) { @@ -374,13 +340,12 @@ public class AliYun { public String getDownloadUrl(String fileId) { try { SpiderDebug.log("getDownloadUrl..." + fileId); - if (getDriveId()) throw new Exception("unable obtain drive id"); tempIds.add(0, copy(fileId)); - JSONObject body = new JSONObject(); - body.put("file_id", tempIds.get(0)); - body.put("drive_id", driveId); - String json = oauth("openFile/getDownloadUrl", body.toString(), true); - return new JSONObject(json).getString("url"); + JsonObject param = new JsonObject(); + param.addProperty("file_id", tempIds.get(0)); + param.addProperty("drive_id", drive.getDriveId()); + String json = oauth("openFile/getDownloadUrl", param.toString(), true); + return Download.objectFrom(json).getUrl(); } catch (Exception e) { e.printStackTrace(); return ""; @@ -389,21 +354,20 @@ public class AliYun { } } - public JSONObject getVideoPreviewPlayInfo(String fileId) { + public Preview.Info getVideoPreviewPlayInfo(String fileId) { try { SpiderDebug.log("getVideoPreviewPlayInfo..." + fileId); - if (getDriveId()) throw new Exception("unable obtain drive id"); tempIds.add(0, copy(fileId)); - JSONObject body = new JSONObject(); - body.put("file_id", tempIds.get(0)); - body.put("drive_id", driveId); - body.put("category", "live_transcoding"); - body.put("url_expire_sec", "14400"); - String json = oauth("openFile/getVideoPreviewPlayInfo", body.toString(), true); - return new JSONObject(json).getJSONObject("video_preview_play_info"); + JsonObject param = new JsonObject(); + param.addProperty("file_id", tempIds.get(0)); + param.addProperty("drive_id", drive.getDriveId()); + param.addProperty("category", "live_transcoding"); + param.addProperty("url_expire_sec", "14400"); + String json = oauth("openFile/getVideoPreviewPlayInfo", param.toString(), true); + return Preview.objectFrom(json).getVideoPreviewPlayInfo(); } catch (Exception e) { e.printStackTrace(); - return new JSONObject(); + return new Preview.Info(); } finally { Init.execute(this::deleteAll); } @@ -415,51 +379,36 @@ public class AliYun { } private String getPreviewContent(String[] ids) { - try { - JSONObject playInfo = getVideoPreviewPlayInfo(ids[0]); - List url = getPreviewUrl(playInfo); - List subs = getSubs(ids); - subs.addAll(getSubs(playInfo)); - return Result.get().url(url).m3u8().subs(subs).header(getHeader()).string(); - } catch (Exception e) { - e.printStackTrace(); - return Result.get().url("").string(); - } + Preview.Info info = getVideoPreviewPlayInfo(ids[0]); + List url = getPreviewUrl(info); + List subs = getSubs(ids); + subs.addAll(getSubs(info)); + return Result.get().url(url).m3u8().subs(subs).header(getHeader()).string(); } - private List getPreviewUrl(JSONObject playInfo) throws Exception { - if (!playInfo.has("live_transcoding_task_list")) return Collections.emptyList(); - JSONArray taskList = playInfo.getJSONArray("live_transcoding_task_list"); + private List getPreviewUrl(Preview.Info info) { + List tasks = info.getLiveTranscodingTaskList(); List url = new ArrayList<>(); - for (int i = taskList.length() - 1; i >= 0; i--) { - JSONObject task = taskList.getJSONObject(i); - if (!task.optString("status").equals("finished")) continue; - url.add(task.optString("template_id")); - url.add(task.optString("url")); + for (int i = tasks.size() - 1; i >= 0; i--) { + url.add(tasks.get(i).getTemplateId()); + url.add(tasks.get(i).getUrl()); } return url; } - private List getSubs(JSONObject playInfo) throws Exception { - if (!playInfo.has("live_transcoding_subtitle_task_list")) return Collections.emptyList(); - JSONArray taskList = playInfo.getJSONArray("live_transcoding_subtitle_task_list"); + private List getSubs(Preview.Info info) { List subs = new ArrayList<>(); - for (int i = 0; i < taskList.length(); ++i) { - JSONObject task = taskList.getJSONObject(i); - String lang = task.getString("language"); - String url = task.getString("url"); - subs.add(Sub.create().url(url).name(lang).lang(lang).ext("vtt")); - } + for (Preview.LiveTranscodingTask task : info.getLiveTranscodingSubtitleTaskList()) subs.add(task.getSub()); return subs; } - private String copy(String fileId) throws Exception { - SpiderDebug.log("Copy..." + fileId); + private String copy(String fileId) { + if (drive.getDriveId().isEmpty()) getDriveId(); + SpiderDebug.log("Copy..." + fileId + "," + shareId + "," + drive.getDriveId()); String json = "{\"requests\":[{\"body\":{\"file_id\":\"%s\",\"share_id\":\"%s\",\"auto_rename\":true,\"to_parent_file_id\":\"root\",\"to_drive_id\":\"%s\"},\"headers\":{\"Content-Type\":\"application/json\"},\"id\":\"0\",\"method\":\"POST\",\"url\":\"/file/copy\"}],\"resource\":\"file\"}"; - json = String.format(json, fileId, shareId, driveId); - String result = auth("adrive/v2/batch", json, true); - if (result.contains("ForbiddenNoPermission.File")) return copy(fileId); - return new JSONObject(result).getJSONArray("responses").getJSONObject(0).getJSONObject("body").getString("file_id"); + json = String.format(json, fileId, shareId, drive.getDriveId()); + Res res = Res.objectFrom(auth("adrive/v2/batch", json, true)); + return res.getResponse().getBody().getFileId(); } private void deleteAll() { @@ -471,15 +420,11 @@ public class AliYun { } private boolean delete(String fileId) { - try { - SpiderDebug.log("Delete..." + fileId); - String json = "{\"requests\":[{\"body\":{\"drive_id\":\"%s\",\"file_id\":\"%s\"},\"headers\":{\"Content-Type\":\"application/json\"},\"id\":\"%s\",\"method\":\"POST\",\"url\":\"/file/delete\"}],\"resource\":\"file\"}"; - json = String.format(json, driveId, fileId, fileId); - String result = auth("adrive/v2/batch", json, true); - return result.length() == 211; - } catch (Exception ignored) { - return false; - } + SpiderDebug.log("Delete..." + fileId); + String json = "{\"requests\":[{\"body\":{\"drive_id\":\"%s\",\"file_id\":\"%s\"},\"headers\":{\"Content-Type\":\"application/json\"},\"id\":\"%s\",\"method\":\"POST\",\"url\":\"/file/delete\"}],\"resource\":\"file\"}"; + json = String.format(json, drive.getDriveId(), fileId, fileId); + Res res = Res.objectFrom(auth("adrive/v2/batch", json, true)); + return res.getResponse().getStatus() == 404; } public Object[] proxySub(Map params) throws Exception { diff --git a/app/src/main/java/com/github/catvod/bean/Result.java b/app/src/main/java/com/github/catvod/bean/Result.java index 1da6c813..78009793 100644 --- a/app/src/main/java/com/github/catvod/bean/Result.java +++ b/app/src/main/java/com/github/catvod/bean/Result.java @@ -1,6 +1,7 @@ package com.github.catvod.bean; import com.google.gson.Gson; +import com.google.gson.JsonElement; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; @@ -54,6 +55,10 @@ public class Result { return Result.get().classes(classes).vod(list).filters(filters).string(); } + public static String string(List classes, List list, JsonElement filters) { + return Result.get().classes(classes).vod(list).filters(filters).string(); + } + public static String string(List classes, LinkedHashMap> filters) { return Result.get().classes(classes).filters(filters).string(); } @@ -105,6 +110,13 @@ public class Result { return this; } + public Result filters(JsonElement element) { + if (element == null) return this; + Type listType = new TypeToken>>() {}.getType(); + this.filters = new Gson().fromJson(element.toString(), listType); + return this; + } + public Result header(Map header) { if (header.isEmpty()) return this; this.header = new Gson().toJson(header); diff --git a/app/src/main/java/com/github/catvod/bean/Vod.java b/app/src/main/java/com/github/catvod/bean/Vod.java index c986d8a9..63f09c89 100644 --- a/app/src/main/java/com/github/catvod/bean/Vod.java +++ b/app/src/main/java/com/github/catvod/bean/Vod.java @@ -116,7 +116,11 @@ public class Vod { public void setVodPlayUrl(String vodPlayUrl) { this.vodPlayUrl = vodPlayUrl; } - + + public String getVodPlayUrl() { + return vodPlayUrl; + } + public void setVodTag(String vodTag) { this.vodTag = vodTag; } diff --git a/app/src/main/java/com/github/catvod/bean/ali/Download.java b/app/src/main/java/com/github/catvod/bean/ali/Download.java new file mode 100644 index 00000000..c0f8c613 --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/ali/Download.java @@ -0,0 +1,32 @@ +package com.github.catvod.bean.ali; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +public class Download { + + @SerializedName("url") + private String url; + @SerializedName("file_id") + private String fileId; + @SerializedName("expiration") + private String expiration; + + public static Download objectFrom(String str) { + return new Gson().fromJson(str, Download.class); + } + + public String getUrl() { + return TextUtils.isEmpty(url) ? "" : url; + } + + public String getFileId() { + return TextUtils.isEmpty(fileId) ? "" : fileId; + } + + public String getExpiration() { + return TextUtils.isEmpty(expiration) ? "" : expiration; + } +} diff --git a/app/src/main/java/com/github/catvod/bean/ali/Drive.java b/app/src/main/java/com/github/catvod/bean/ali/Drive.java index 58bcb9e0..acf2d9a9 100644 --- a/app/src/main/java/com/github/catvod/bean/ali/Drive.java +++ b/app/src/main/java/com/github/catvod/bean/ali/Drive.java @@ -10,43 +10,29 @@ import com.google.gson.annotations.SerializedName; public class Drive { @SerializedName("default_drive_id") - private String driveId; - @SerializedName("user_id") - private String userId; - @SerializedName("backup_drive_id") - private String backupDriveId; + private String defaultDriveId; @SerializedName("resource_drive_id") private String resourceDriveId; - @SerializedName("sbox_drive_id") - private String sboxDriveId; public static Drive objectFrom(String str) { Drive item = new Gson().fromJson(str, Drive.class); return item == null ? new Drive() : item; } - public String getDriveId() { - return TextUtils.isEmpty(driveId) ? "" : driveId; + private String getDefaultDriveId() { + return TextUtils.isEmpty(defaultDriveId) ? "" : defaultDriveId; } - public String getUserId() { - return TextUtils.isEmpty(userId) ? "" : userId; - } - - public String getBackupDriveId() { - return TextUtils.isEmpty(backupDriveId) ? "" : backupDriveId; - } - - public String getResourceDriveId() { + private String getResourceDriveId() { return TextUtils.isEmpty(resourceDriveId) ? "" : resourceDriveId; } - public String getSboxDriveId() { - return TextUtils.isEmpty(sboxDriveId) ? "" : sboxDriveId; + public String getDriveId() { + return getResourceDriveId().isEmpty() ? getDefaultDriveId() : getResourceDriveId(); } public Drive clean() { - this.driveId = ""; + this.defaultDriveId = ""; this.resourceDriveId = ""; return this; } diff --git a/app/src/main/java/com/github/catvod/bean/ali/Item.java b/app/src/main/java/com/github/catvod/bean/ali/Item.java index 0026a3d8..47715fd1 100644 --- a/app/src/main/java/com/github/catvod/bean/ali/Item.java +++ b/app/src/main/java/com/github/catvod/bean/ali/Item.java @@ -2,6 +2,7 @@ package com.github.catvod.bean.ali; import android.text.TextUtils; +import com.github.catvod.utils.ChineseComparator; import com.github.catvod.utils.Utils; import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; @@ -85,9 +86,9 @@ public class Item implements Comparable { public String getDisplayName() { return TextUtils.join(" ", Arrays.asList(getParent(), getName(), getSize())).trim(); } - + @Override public int compareTo(Item item) { - return this.getName().compareTo(item.getName()); + return ChineseComparator.compare(this.getDisplayName(), item.getDisplayName()); } } diff --git a/app/src/main/java/com/github/catvod/bean/ali/Preview.java b/app/src/main/java/com/github/catvod/bean/ali/Preview.java new file mode 100644 index 00000000..0d6c6a3a --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/ali/Preview.java @@ -0,0 +1,84 @@ +package com.github.catvod.bean.ali; + +import android.text.TextUtils; + +import com.github.catvod.bean.Sub; +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +import java.util.Collections; +import java.util.List; + +public class Preview { + + @SerializedName("video_preview_play_info") + private Info videoPreviewPlayInfo; + @SerializedName("drive_id") + private String driveId; + @SerializedName("file_id") + private String fileId; + + public static Preview objectFrom(String str) { + return new Gson().fromJson(str, Preview.class); + } + + public Info getVideoPreviewPlayInfo() { + return videoPreviewPlayInfo == null ? new Info() : videoPreviewPlayInfo; + } + + public String getDriveId() { + return TextUtils.isEmpty(driveId) ? "" : driveId; + } + + public String getFileId() { + return TextUtils.isEmpty(fileId) ? "" : fileId; + } + + public static class Info { + + @SerializedName("live_transcoding_task_list") + private List liveTranscodingTaskList; + @SerializedName("live_transcoding_subtitle_task_list") + private List liveTranscodingSubtitleTaskList; + + public List getLiveTranscodingTaskList() { + return liveTranscodingTaskList == null ? Collections.emptyList() : liveTranscodingTaskList; + } + + public List getLiveTranscodingSubtitleTaskList() { + return liveTranscodingSubtitleTaskList == null ? Collections.emptyList() : liveTranscodingSubtitleTaskList; + } + } + + public static class LiveTranscodingTask { + + @SerializedName("template_id") + private String templateId; + @SerializedName("language") + private String language; + @SerializedName("status") + private String status; + @SerializedName("url") + private String url; + + public String getTemplateId() { + return TextUtils.isEmpty(templateId) ? "" : templateId; + } + + public String getLanguage() { + return TextUtils.isEmpty(language) ? "" : language; + } + + public String getStatus() { + return TextUtils.isEmpty(status) ? "" : status; + } + + public String getUrl() { + return TextUtils.isEmpty(url) ? "" : url; + } + + public Sub getSub() { + return Sub.create().url(getUrl()).name(getLanguage()).lang(getLanguage()).ext("vtt"); + } + } +} diff --git a/app/src/main/java/com/github/catvod/bean/ali/Res.java b/app/src/main/java/com/github/catvod/bean/ali/Res.java new file mode 100644 index 00000000..95e7703b --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/ali/Res.java @@ -0,0 +1,79 @@ +package com.github.catvod.bean.ali; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +import java.util.Collections; +import java.util.List; + +public class Res { + + @SerializedName("responses") + private List responses; + @SerializedName("body") + private Body body; + @SerializedName("id") + private String id; + @SerializedName("status") + private int status; + + public static Res objectFrom(String str) { + return new Gson().fromJson(str, Res.class); + } + + public List getResponses() { + return responses == null ? Collections.emptyList() : responses; + } + + public Res getResponse() { + return getResponses().isEmpty() ? new Res() : getResponses().get(0); + } + + public Body getBody() { + return body == null ? new Body() : body; + } + + public String getId() { + return TextUtils.isEmpty(id) ? "" : id; + } + + public int getStatus() { + return status; + } + + public static class Body { + + @SerializedName("domain_id") + private String domainId; + @SerializedName("drive_id") + private String driveId; + @SerializedName("file_id") + private String fileId; + @SerializedName("code") + private String code; + @SerializedName("message") + private String message; + + public String getDomainId() { + return TextUtils.isEmpty(domainId) ? "" : domainId; + } + + public String getDriveId() { + return TextUtils.isEmpty(driveId) ? "" : driveId; + } + + public String getFileId() { + return TextUtils.isEmpty(fileId) ? "" : fileId; + } + + public String getCode() { + return TextUtils.isEmpty(code) ? "" : code; + } + + public String getMessage() { + return TextUtils.isEmpty(message) ? "" : message; + } + } +} diff --git a/app/src/main/java/com/github/catvod/bean/ali/Share.java b/app/src/main/java/com/github/catvod/bean/ali/Share.java new file mode 100644 index 00000000..457123b6 --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/ali/Share.java @@ -0,0 +1,108 @@ +package com.github.catvod.bean.ali; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +import java.util.Collections; +import java.util.List; + +public class Share { + + @SerializedName("share_token") + private String shareToken; + @SerializedName("expire_time") + private String expireTime; + @SerializedName("expires_in") + private int expiresIn; + + @SerializedName("creator_id") + private String creatorId; + @SerializedName("creator_name") + private String creatorName; + @SerializedName("creator_phone") + private String creatorPhone; + @SerializedName("expiration") + private String expiration; + @SerializedName("updated_at") + private String updatedAt; + @SerializedName("vip") + private String vip; + @SerializedName("avatar") + private String avatar; + @SerializedName("share_name") + private String shareName; + @SerializedName("display_name") + private String displayName; + @SerializedName("share_title") + private String shareTitle; + @SerializedName("has_pwd") + private boolean hasPwd; + @SerializedName("file_infos") + private List fileInfos; + + public static Share objectFrom(String str) { + return new Gson().fromJson(str, Share.class); + } + + public String getShareToken() { + return TextUtils.isEmpty(shareToken) ? "" : shareToken; + } + + public String getExpireTime() { + return TextUtils.isEmpty(expireTime) ? "" : expireTime; + } + + public int getExpiresIn() { + return expiresIn; + } + + public String getCreatorId() { + return TextUtils.isEmpty(creatorId) ? "" : creatorId; + } + + public String getCreatorName() { + return TextUtils.isEmpty(creatorName) ? "" : creatorName; + } + + public String getCreatorPhone() { + return TextUtils.isEmpty(creatorPhone) ? "" : creatorPhone; + } + + public String getExpiration() { + return TextUtils.isEmpty(expiration) ? "" : expiration; + } + + public String getUpdatedAt() { + return TextUtils.isEmpty(updatedAt) ? "" : updatedAt; + } + + public String getVip() { + return TextUtils.isEmpty(vip) ? "" : vip; + } + + public String getAvatar() { + return TextUtils.isEmpty(avatar) ? "" : avatar; + } + + public String getShareName() { + return TextUtils.isEmpty(shareName) ? "" : shareName; + } + + public String getDisplayName() { + return TextUtils.isEmpty(displayName) ? "" : displayName; + } + + public String getShareTitle() { + return TextUtils.isEmpty(shareTitle) ? "" : shareTitle; + } + + public boolean isHasPwd() { + return hasPwd; + } + + public List getFileInfos() { + return fileInfos == null ? Collections.emptyList() : fileInfos; + } +} diff --git a/app/src/main/java/com/github/catvod/spider/Ali.java b/app/src/main/java/com/github/catvod/spider/Ali.java index 39f9d194..476c1982 100644 --- a/app/src/main/java/com/github/catvod/spider/Ali.java +++ b/app/src/main/java/com/github/catvod/spider/Ali.java @@ -1,12 +1,17 @@ package com.github.catvod.spider; import android.content.Context; +import android.text.TextUtils; import com.github.catvod.api.AliYun; import com.github.catvod.bean.Result; +import com.github.catvod.bean.Vod; import com.github.catvod.crawler.Spider; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -25,24 +30,59 @@ public class Ali extends Spider { @Override public String detailContent(List ids) throws Exception { - String url = ids.get(0).trim(); - Matcher matcher = pattern.matcher(url); - if (!matcher.find()) return ""; - String shareId = matcher.group(1); - String fileId = matcher.groupCount() == 3 ? matcher.group(3) : ""; - AliYun.get().setShareId(shareId); - return Result.string(AliYun.get().getVod(url, fileId)); + String id = ids.get(0).trim(); + Matcher matcher = pattern.matcher(id); + return matcher.find() ? Result.string(parseVod(matcher, id)) : ""; } @Override public String playerContent(String flag, String id, List vipFlags) { - return AliYun.get().playerContent(id.split("\\+"), flag.equals("原畫")); + AliYun.get().setShareId(id.split("@")[0]); + return AliYun.get().playerContent(id.split("@")[1].split("\\+"), flag.split("#")[0].equals("原畫")); + } + + private Vod parseVod(Matcher matcher, String id) { + String shareId = matcher.group(1); + String fileId = matcher.groupCount() == 3 ? matcher.group(3) : ""; + AliYun.get().setShareId(shareId); + return AliYun.get().getVod(id, fileId); + } + + /** + * 獲取詳情內容視頻播放來源(多 shared_link) + * + * @param ids share_link 集合 + * @return 詳情內容視頻播放來源 + */ + public String detailContentVodPlayFrom(List ids) { + List playFrom = new ArrayList<>(); + if (ids.size() < 2) return TextUtils.join("$$$", Arrays.asList("原畫", "普畫")); + for (int i = 1; i <= ids.size(); i++) { + playFrom.add(String.format(Locale.getDefault(), "原畫#%02d", i)); + playFrom.add(String.format(Locale.getDefault(), "普畫#%02d", i)); + } + return TextUtils.join("$$$", playFrom); + } + + /** + * 獲取詳情內容視頻播放地址(多 share_link) + * + * @param ids share_link 集合 + * @return 詳情內容視頻播放地址 + */ + public String detailContentVodPlayUrl(List ids) { + List playUrl = new ArrayList<>(); + for (String id : ids) { + Matcher matcher = pattern.matcher(id); + if (matcher.find()) playUrl.add(parseVod(matcher, id).getVodPlayUrl()); + } + return TextUtils.join("$$$", playUrl); } public static Object[] proxy(Map params) throws Exception { String type = params.get("type"); - if (type.equals("sub")) return AliYun.get().proxySub(params); - if (type.equals("token")) return AliYun.get().getToken(); + if ("sub".equals(type)) return AliYun.get().proxySub(params); + if ("token".equals(type)) return AliYun.get().getToken(); return null; } } \ No newline at end of file diff --git a/app/src/main/java/com/github/catvod/spider/Wogg.java b/app/src/main/java/com/github/catvod/spider/Wogg.java index f0d7460f..0ea10c14 100644 --- a/app/src/main/java/com/github/catvod/spider/Wogg.java +++ b/app/src/main/java/com/github/catvod/spider/Wogg.java @@ -2,18 +2,21 @@ package com.github.catvod.spider; import android.content.Context; +import com.github.catvod.bean.Class; import com.github.catvod.bean.Result; import com.github.catvod.bean.Vod; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Utils; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.net.URLEncoder; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -25,9 +28,11 @@ import java.util.regex.Pattern; */ public class Wogg extends Ali { - private final String siteURL = "http://tvfan.xxooo.cf"; + private final String siteUrl = "http://tvfan.xxooo.cf"; + private final Pattern regexCategory = Pattern.compile("/vodtype/(\\w+).html"); + private final Pattern regexPageTotal = Pattern.compile("\\$\\(\"\\.mac_total\"\\)\\.text\\('(\\d+)'\\);"); - private final Pattern regexAli = Pattern.compile("(https://www.aliyundrive.com/s/[^\"]+)"); + private JsonObject extend; private Map getHeader() { Map header = new HashMap<>(); @@ -37,14 +42,87 @@ public class Wogg extends Ali { @Override public void init(Context context, String extend) { - super.init(context, extend); + this.extend = JsonParser.parseString(extend).getAsJsonObject(); + super.init(context, this.extend.get("token").getAsString()); + } + + @Override + public String homeContent(boolean filter) { + List classes = new ArrayList<>(); + String url = extend.get("filter").getAsString(); + Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader())); + Elements elements = doc.select(".nav-link"); + for (Element e : elements) { + Matcher mather = regexCategory.matcher(e.attr("href")); + if (mather.find()) { + classes.add(new Class(mather.group(1), e.text().trim())); + } + } + return Result.string(classes, parseVodListFromDoc(doc), filter ? JsonParser.parseString(OkHttp.string(url)) : null); + } + + @Override + public String categoryContent(String tid, String pg, boolean filter, HashMap extend) { + String[] urlParams = new String[]{tid, "", "", "", "", "", "", "", pg, "", "", ""}; + if (extend != null && extend.size() > 0) { + for (String key : extend.keySet()) { + urlParams[Integer.parseInt(key)] = extend.get(key); + } + } + Document doc = Jsoup.parse(OkHttp.string(String.format("%s/index.php/vodshow/%s.html", siteUrl, String.join("-", urlParams)), getHeader())); + int page = Integer.parseInt(pg), limit = 72, total = 0; + Matcher matcher = regexPageTotal.matcher(doc.html()); + if (matcher.find()) total = Integer.parseInt(matcher.group(1)); + int count = total <= limit ? 1 : ((int) Math.ceil(total / (double) limit)); + return Result.get().vod(parseVodListFromDoc(doc)).page(page, count, limit, total).string(); + } + + private List parseVodListFromDoc(Document doc) { + List list = new ArrayList<>(); + Elements elements = doc.select(".module-item"); + for (Element e : elements) { + String vodId = e.selectFirst(".video-name a").attr("href"); + String vodPic = e.selectFirst(".module-item-pic > img").attr("data-src"); + String vodName = e.selectFirst(".video-name").text(); + String vodRemarks = e.selectFirst(".module-item-text").text(); + list.add(new Vod(vodId, vodName, vodPic, vodRemarks)); + } + return list; } @Override public String detailContent(List ids) throws Exception { - Matcher matcher = regexAli.matcher(OkHttp.string(siteURL + ids.get(0), getHeader())); - if (matcher.find()) return super.detailContent(Arrays.asList(matcher.group(1))); - return ""; + String vodId = ids.get(0); + Document doc = Jsoup.parse(OkHttp.string(siteUrl + vodId, getHeader())); + + Vod item = new Vod(); + item.setVodId(vodId); + item.setVodName(doc.selectFirst(".video-info-header > .page-title").text()); + item.setVodPic(doc.selectFirst(".module-item-pic img").attr("data-src")); + item.setVodArea(doc.select(".video-info-header a.tag-link").last().text()); + item.setTypeName(String.join(",", doc.select(".video-info-header div.tag-link a").eachText())); + + List shareLinks = doc.select(".module-row-text").eachAttr("data-clipboard-text"); + item.setVodPlayFrom(super.detailContentVodPlayFrom(shareLinks)); + item.setVodPlayUrl(super.detailContentVodPlayUrl(shareLinks)); + + Elements elements = doc.select(".video-info-item"); + for (Element e : elements) { + String title = e.previousElementSibling().text(); + if (title.contains("导演")) { + item.setVodDirector(String.join(",", e.select("a").eachText())); + } else if (title.contains("主演")) { + item.setVodActor(String.join(",", e.select("a").eachText())); + } else if (title.contains("年代")) { + item.setVodYear(e.selectFirst("a").text().trim()); + } else if (title.contains("备注")) { + item.setVodRemarks(e.text().trim()); + } else if (title.contains("剧情")) { + item.setVodContent(e.selectFirst(".sqjj_a").text().replace("[收起部分]", "").trim()); + } + } + + return Result.string(item); } @Override @@ -58,7 +136,7 @@ public class Wogg extends Ali { } private String searchContent(String key, String pg) { - String searchURL = siteURL + String.format("/index.php/vodsearch/%s----------%s---.html", URLEncoder.encode(key), pg); + String searchURL = siteUrl + String.format("/index.php/vodsearch/%s----------%s---.html", URLEncoder.encode(key), pg); String html = OkHttp.string(searchURL, getHeader()); Elements items = Jsoup.parse(html).select(".module-search-item"); List list = new ArrayList<>(); diff --git a/app/src/main/java/com/github/catvod/utils/ChineseComparator.java b/app/src/main/java/com/github/catvod/utils/ChineseComparator.java new file mode 100644 index 00000000..bcc45ec1 --- /dev/null +++ b/app/src/main/java/com/github/catvod/utils/ChineseComparator.java @@ -0,0 +1,117 @@ +package com.github.catvod.utils; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * 按照 Windows 排序风格,对给定的数字、字母、汉字字符串进行排序 + * + * @author Oiltea + */ +public class ChineseComparator { + + private static final Integer TYPE_NUMBER = 0; + private static final Integer TYPE_CHARACTER = 1; + + public static int compare(String o1, String o2) { + // 根据字符数组生成带分类的字符列表 + // List的第一位为该字符的类型(TYPE_NUMBER, TYPE_CHARACTER) + // List的第二位为该字符的内容(一位数字, 一位非数字, 多位数字) + List> o1CharList = getCharList(o1); + List> o2CharList = getCharList(o2); + + // 统一CharList的长度 + int max = Math.max(o1CharList.size(), o2CharList.size()); + while (o1CharList.size() < max) { + o1CharList.add(new ArrayList<>()); + } + while (o2CharList.size() < max) { + o2CharList.add(new ArrayList<>()); + } + + // 开始比较 + int compare = 0; + for (int i = 0; i < max; i++) { + List o1list = o1CharList.get(i); + List o2list = o2CharList.get(i); + + // CharList短的,排在前面 + if (o1list.size() == 0) { + compare = -1; + break; + } + if (o2list.size() == 0) { + compare = 1; + break; + } + + // 先比较类型 + Integer o1Type = (Integer) o1list.get(0); + Integer o2Type = (Integer) o2list.get(0); + int typeCompare = Integer.compare(o1Type, o2Type); + if (typeCompare != 0) { + // 类型不同,则数字在前,非数字在后 + compare = typeCompare; + break; + } else { + // 类型相同,则比较内容 + if (TYPE_NUMBER.equals(o1Type)) { + // 比较数字 + int o1Content = Integer.parseInt(o1list.get(1).toString()); + int o2Content = Integer.parseInt(o2list.get(1).toString()); + compare = Integer.compare(o1Content, o2Content); + } else if (TYPE_CHARACTER.equals(o1Type)) { + // 比较非数字 + String o1Content = (String) o1list.get(1); + String o2Content = (String) o2list.get(1); + compare = Collator.getInstance(Locale.CHINESE).compare(o1Content, o2Content); + } + // 如果不相等,则退出比较 + if (compare != 0) { + break; + } + } + } + return compare; + } + + /** + * 根据字符数组生成带分类的字符列表 + * + * @param text 字符串 + * @return 带分类的字符列表,List的第一位为该字符的类型(TYPE_NUMBER, TYPE_CHARACTER),第二位为该字符的内容 + */ + private static List> getCharList(String text) { + char[] chars = text.toCharArray(); + List> charList = new ArrayList<>(); + for (int i = 0; i < chars.length; i++) { + List list = new ArrayList<>(); + // 是否为数字 + char c = chars[i]; + if ((int) c >= '0' && (int) c <= '9') { + StringBuilder str = new StringBuilder(); + // 下一位是否为数字,如果为数字则组成多位数 + do { + str.append(c); + if (i + 1 < chars.length) { + c = chars[++i]; + } else { + break; + } + } while ((int) c >= '0' && (int) c <= '9'); + if (!(i + 1 == chars.length) || !((int) c >= '0' && (int) c <= '9')) { + i--; + } + list.add(TYPE_NUMBER); + list.add(str.toString()); + } else { + list.add(TYPE_CHARACTER); + list.add(String.valueOf(c)); + } + charList.add(list); + } + return charList; + } +} \ No newline at end of file diff --git a/jar/custom_spider.jar b/jar/custom_spider.jar index 205c0fad..e55093ea 100644 Binary files a/jar/custom_spider.jar and b/jar/custom_spider.jar differ diff --git a/jar/custom_spider.jar.md5 b/jar/custom_spider.jar.md5 index 94ae6e92..8782773f 100644 --- a/jar/custom_spider.jar.md5 +++ b/jar/custom_spider.jar.md5 @@ -1 +1 @@ -dede7a67d11523b79d649c152910fd6a +a2204506315000174d11fe509f88259c diff --git a/json/adult.json b/json/adult.json index 5615c54b..0cee2c78 100644 --- a/json/adult.json +++ b/json/adult.json @@ -1,5 +1,5 @@ { - "spider": "https://fongmi.cachefly.net/FongMi/CatVodSpider/main/jar/custom_spider.jar;md5;dede7a67d11523b79d649c152910fd6a", + "spider": "https://fongmi.cachefly.net/FongMi/CatVodSpider/main/jar/custom_spider.jar;md5;a2204506315000174d11fe509f88259c", "wallpaper": "https://gao.chuqiuyu.tk", "sites": [ { diff --git a/json/config.json b/json/config.json index 2ec687cc..b416a368 100644 --- a/json/config.json +++ b/json/config.json @@ -1,5 +1,5 @@ { - "spider": "https://fongmi.cachefly.net/FongMi/CatVodSpider/main/jar/custom_spider.jar;md5;dede7a67d11523b79d649c152910fd6a", + "spider": "https://fongmi.cachefly.net/FongMi/CatVodSpider/main/jar/custom_spider.jar;md5;a2204506315000174d11fe509f88259c", "wallpaper": "http://饭太硬.top/深色壁纸/api.php", "sites": [ { @@ -65,6 +65,18 @@ "searchable": 1, "changeable": 1 }, + { + "key": "玩偶", + "name": "玩偶", + "type": 3, + "api": "csp_Wogg", + "searchable": 1, + "changeable": 0, + "ext": { + "token": "影視天下第一", + "filter": "https://fongmi.cachefly.net/FongMi/CatVodSpider/main/json/wogg.json" + } + }, { "key": "星星", "name": "星星", @@ -146,14 +158,6 @@ "searchable": 1, "changeable": 1 }, - { - "key": "玩偶", - "name": "玩偶", - "type": 3, - "api": "csp_Wogg", - "searchable": 1, - "changeable": 0 - }, { "key": "七夜", "name": "七夜", diff --git a/json/wogg.json b/json/wogg.json new file mode 100644 index 00000000..886c1c27 --- /dev/null +++ b/json/wogg.json @@ -0,0 +1,1318 @@ +{ + "1": [ + { + "key": "3", + "name": "剧情", + "value": [ + { + "n": "喜剧", + "v": "喜剧" + }, + { + "n": "爱情", + "v": "爱情" + }, + { + "n": "恐怖", + "v": "恐怖" + }, + { + "n": "动作", + "v": "动作" + }, + { + "n": "科幻", + "v": "科幻" + }, + { + "n": "剧情", + "v": "剧情" + }, + { + "n": "战争", + "v": "战争" + }, + { + "n": "警匪", + "v": "警匪" + }, + { + "n": "犯罪", + "v": "犯罪" + }, + { + "n": "动画", + "v": "动画" + }, + { + "n": "奇幻", + "v": "奇幻" + }, + { + "n": "武侠", + "v": "武侠" + }, + { + "n": "冒险", + "v": "冒险" + }, + { + "n": "枪战", + "v": "枪战" + }, + { + "n": "恐怖", + "v": "恐怖" + }, + { + "n": "悬疑", + "v": "悬疑" + }, + { + "n": "惊悚", + "v": "惊悚" + }, + { + "n": "经典", + "v": "经典" + }, + { + "n": "青春", + "v": "青春" + }, + { + "n": "文艺", + "v": "文艺" + }, + { + "n": "古装", + "v": "古装" + }, + { + "n": "历史", + "v": "历史" + }, + { + "n": "微电影", + "v": "微电影" + } + ] + }, + { + "key": "1", + "name": "地区", + "value": [ + { + "n": "大陆", + "v": "大陆" + }, + { + "n": "香港", + "v": "香港" + }, + { + "n": "台湾", + "v": "台湾" + }, + { + "n": "美国", + "v": "美国" + }, + { + "n": "法国", + "v": "法国" + }, + { + "n": "英国", + "v": "英国" + }, + { + "n": "日本", + "v": "日本" + }, + { + "n": "韩国", + "v": "韩国" + }, + { + "n": "德国", + "v": "德国" + }, + { + "n": "泰国", + "v": "泰国" + }, + { + "n": "印度", + "v": "印度" + }, + { + "n": "意大利", + "v": "意大利" + }, + { + "n": "西班牙", + "v": "西班牙" + }, + { + "n": "加拿大", + "v": "加拿大" + }, + { + "n": "其他", + "v": "其他" + } + ] + }, + { + "key": "11", + "name": "年份", + "value": [ + { + "n": "2023", + "v": "2023" + }, + { + "n": "2022", + "v": "2022" + }, + { + "n": "2021", + "v": "2021" + }, + { + "n": "2020", + "v": "2020" + }, + { + "n": "2019", + "v": "2019" + }, + { + "n": "2018", + "v": "2018" + }, + { + "n": "2017", + "v": "2017" + }, + { + "n": "2016", + "v": "2016" + }, + { + "n": "2015", + "v": "2015" + }, + { + "n": "2014", + "v": "2014" + }, + { + "n": "2013", + "v": "2013" + }, + { + "n": "2012", + "v": "2012" + }, + { + "n": "2011", + "v": "2011" + }, + { + "n": "2010", + "v": "2010" + } + ] + }, + { + "key": "5", + "name": "字母", + "value": [ + { + "n": "A", + "v": "A" + }, + { + "n": "B", + "v": "B" + }, + { + "n": "C", + "v": "C" + }, + { + "n": "D", + "v": "D" + }, + { + "n": "E", + "v": "E" + }, + { + "n": "F", + "v": "F" + }, + { + "n": "G", + "v": "G" + }, + { + "n": "H", + "v": "H" + }, + { + "n": "I", + "v": "I" + }, + { + "n": "J", + "v": "J" + }, + { + "n": "K", + "v": "K" + }, + { + "n": "L", + "v": "L" + }, + { + "n": "M", + "v": "M" + }, + { + "n": "N", + "v": "N" + }, + { + "n": "O", + "v": "O" + }, + { + "n": "P", + "v": "P" + }, + { + "n": "Q", + "v": "Q" + }, + { + "n": "R", + "v": "R" + }, + { + "n": "S", + "v": "S" + }, + { + "n": "T", + "v": "T" + }, + { + "n": "U", + "v": "U" + }, + { + "n": "V", + "v": "V" + }, + { + "n": "W", + "v": "W" + }, + { + "n": "X", + "v": "X" + }, + { + "n": "Y", + "v": "Y" + }, + { + "n": "Z", + "v": "Z" + }, + { + "n": "0-9", + "v": "0-9" + } + ] + }, + { + "key": "2", + "name": "排序", + "value": [ + { + "n": "时间", + "v": "time" + }, + { + "n": "人气", + "v": "hits" + }, + { + "n": "评分", + "v": "score" + } + ] + } + ], + "20": [ + { + "key": "1", + "name": "地区", + "value": [ + { + "n": "大陆", + "v": "大陆" + }, + { + "n": "香港", + "v": "香港" + }, + { + "n": "台湾", + "v": "台湾" + }, + { + "n": "美国", + "v": "美国" + }, + { + "n": "法国", + "v": "法国" + }, + { + "n": "英国", + "v": "英国" + }, + { + "n": "日本", + "v": "日本" + }, + { + "n": "韩国", + "v": "韩国" + }, + { + "n": "德国", + "v": "德国" + }, + { + "n": "泰国", + "v": "泰国" + }, + { + "n": "印度", + "v": "印度" + }, + { + "n": "意大利", + "v": "意大利" + }, + { + "n": "西班牙", + "v": "西班牙" + }, + { + "n": "加拿大", + "v": "加拿大" + }, + { + "n": "其他", + "v": "其他" + } + ] + }, + { + "key": "4", + "name": "语言", + "value": [ + { + "n": "国语", + "v": "国语" + }, + { + "n": "英语", + "v": "英语" + }, + { + "n": "粤语", + "v": "粤语" + }, + { + "n": "闽南语", + "v": "闽南语" + }, + { + "n": "韩语", + "v": "韩语" + }, + { + "n": "日语", + "v": "日语" + }, + { + "n": "法语", + "v": "法语" + }, + { + "n": "德语", + "v": "德语" + }, + { + "n": "其它", + "v": "其它" + } + ] + }, + { + "key": "11", + "name": "年份", + "value": [ + { + "n": "2023", + "v": "2023" + }, + { + "n": "2022", + "v": "2022" + }, + { + "n": "2021", + "v": "2021" + }, + { + "n": "2020", + "v": "2020" + }, + { + "n": "2019", + "v": "2019" + }, + { + "n": "2018", + "v": "2018" + }, + { + "n": "2017", + "v": "2017" + }, + { + "n": "2016", + "v": "2016" + }, + { + "n": "2015", + "v": "2015" + }, + { + "n": "2014", + "v": "2014" + }, + { + "n": "2013", + "v": "2013" + }, + { + "n": "2012", + "v": "2012" + }, + { + "n": "2011", + "v": "2011" + }, + { + "n": "2010", + "v": "2010" + } + ] + }, + { + "key": "5", + "name": "字母", + "value": [ + { + "n": "A", + "v": "A" + }, + { + "n": "B", + "v": "B" + }, + { + "n": "C", + "v": "C" + }, + { + "n": "D", + "v": "D" + }, + { + "n": "E", + "v": "E" + }, + { + "n": "F", + "v": "F" + }, + { + "n": "G", + "v": "G" + }, + { + "n": "H", + "v": "H" + }, + { + "n": "I", + "v": "I" + }, + { + "n": "J", + "v": "J" + }, + { + "n": "K", + "v": "K" + }, + { + "n": "L", + "v": "L" + }, + { + "n": "M", + "v": "M" + }, + { + "n": "N", + "v": "N" + }, + { + "n": "O", + "v": "O" + }, + { + "n": "P", + "v": "P" + }, + { + "n": "Q", + "v": "Q" + }, + { + "n": "R", + "v": "R" + }, + { + "n": "S", + "v": "S" + }, + { + "n": "T", + "v": "T" + }, + { + "n": "U", + "v": "U" + }, + { + "n": "V", + "v": "V" + }, + { + "n": "W", + "v": "W" + }, + { + "n": "X", + "v": "X" + }, + { + "n": "Y", + "v": "Y" + }, + { + "n": "Z", + "v": "Z" + }, + { + "n": "0-9", + "v": "0-9" + } + ] + }, + { + "key": "2", + "name": "排序", + "value": [ + { + "n": "时间", + "v": "time" + }, + { + "n": "人气", + "v": "hits" + }, + { + "n": "评分", + "v": "score" + } + ] + } + ], + "24": [ + { + "key": "1", + "name": "地区", + "value": [ + { + "n": "大陆", + "v": "大陆" + }, + { + "n": "香港", + "v": "香港" + }, + { + "n": "台湾", + "v": "台湾" + }, + { + "n": "美国", + "v": "美国" + }, + { + "n": "法国", + "v": "法国" + }, + { + "n": "英国", + "v": "英国" + }, + { + "n": "日本", + "v": "日本" + }, + { + "n": "韩国", + "v": "韩国" + }, + { + "n": "德国", + "v": "德国" + }, + { + "n": "泰国", + "v": "泰国" + }, + { + "n": "印度", + "v": "印度" + }, + { + "n": "意大利", + "v": "意大利" + }, + { + "n": "西班牙", + "v": "西班牙" + }, + { + "n": "加拿大", + "v": "加拿大" + }, + { + "n": "其他", + "v": "其他" + } + ] + }, + { + "key": "4", + "name": "语言", + "value": [ + { + "n": "国语", + "v": "国语" + }, + { + "n": "英语", + "v": "英语" + }, + { + "n": "粤语", + "v": "粤语" + }, + { + "n": "闽南语", + "v": "闽南语" + }, + { + "n": "韩语", + "v": "韩语" + }, + { + "n": "日语", + "v": "日语" + }, + { + "n": "法语", + "v": "法语" + }, + { + "n": "德语", + "v": "德语" + }, + { + "n": "其它", + "v": "其它" + } + ] + }, + { + "key": "11", + "name": "年份", + "value": [ + { + "n": "2023", + "v": "2023" + }, + { + "n": "2022", + "v": "2022" + }, + { + "n": "2021", + "v": "2021" + }, + { + "n": "2020", + "v": "2020" + }, + { + "n": "2019", + "v": "2019" + }, + { + "n": "2018", + "v": "2018" + }, + { + "n": "2017", + "v": "2017" + }, + { + "n": "2016", + "v": "2016" + }, + { + "n": "2015", + "v": "2015" + }, + { + "n": "2014", + "v": "2014" + }, + { + "n": "2013", + "v": "2013" + }, + { + "n": "2012", + "v": "2012" + }, + { + "n": "2011", + "v": "2011" + }, + { + "n": "2010", + "v": "2010" + } + ] + }, + { + "key": "5", + "name": "字母", + "value": [ + { + "n": "A", + "v": "A" + }, + { + "n": "B", + "v": "B" + }, + { + "n": "C", + "v": "C" + }, + { + "n": "D", + "v": "D" + }, + { + "n": "E", + "v": "E" + }, + { + "n": "F", + "v": "F" + }, + { + "n": "G", + "v": "G" + }, + { + "n": "H", + "v": "H" + }, + { + "n": "I", + "v": "I" + }, + { + "n": "J", + "v": "J" + }, + { + "n": "K", + "v": "K" + }, + { + "n": "L", + "v": "L" + }, + { + "n": "M", + "v": "M" + }, + { + "n": "N", + "v": "N" + }, + { + "n": "O", + "v": "O" + }, + { + "n": "P", + "v": "P" + }, + { + "n": "Q", + "v": "Q" + }, + { + "n": "R", + "v": "R" + }, + { + "n": "S", + "v": "S" + }, + { + "n": "T", + "v": "T" + }, + { + "n": "U", + "v": "U" + }, + { + "n": "V", + "v": "V" + }, + { + "n": "W", + "v": "W" + }, + { + "n": "X", + "v": "X" + }, + { + "n": "Y", + "v": "Y" + }, + { + "n": "Z", + "v": "Z" + }, + { + "n": "0-9", + "v": "0-9" + } + ] + }, + { + "key": "2", + "name": "排序", + "value": [ + { + "n": "时间", + "v": "time" + }, + { + "n": "人气", + "v": "hits" + }, + { + "n": "评分", + "v": "score" + } + ] + } + ], + "28": [ + { + "key": "1", + "name": "地区", + "value": [ + { + "n": "国产", + "v": "国产" + }, + { + "n": "日韩", + "v": "日韩" + }, + { + "n": "欧美", + "v": "欧美" + } + ] + }, + { + "key": "11", + "name": "年份", + "value": [ + { + "n": "2023", + "v": "2023" + }, + { + "n": "2022", + "v": "2022" + }, + { + "n": "2021", + "v": "2021" + }, + { + "n": "2020", + "v": "2020" + }, + { + "n": "2019", + "v": "2019" + }, + { + "n": "2018", + "v": "2018" + }, + { + "n": "2017", + "v": "2017" + }, + { + "n": "2016", + "v": "2016" + }, + { + "n": "2015", + "v": "2015" + }, + { + "n": "2014", + "v": "2014" + }, + { + "n": "2013", + "v": "2013" + }, + { + "n": "2012", + "v": "2012" + }, + { + "n": "2011", + "v": "2011" + }, + { + "n": "2010", + "v": "2010" + } + ] + }, + { + "key": "5", + "name": "字母", + "value": [ + { + "n": "A", + "v": "A" + }, + { + "n": "B", + "v": "B" + }, + { + "n": "C", + "v": "C" + }, + { + "n": "D", + "v": "D" + }, + { + "n": "E", + "v": "E" + }, + { + "n": "F", + "v": "F" + }, + { + "n": "G", + "v": "G" + }, + { + "n": "H", + "v": "H" + }, + { + "n": "I", + "v": "I" + }, + { + "n": "J", + "v": "J" + }, + { + "n": "K", + "v": "K" + }, + { + "n": "L", + "v": "L" + }, + { + "n": "M", + "v": "M" + }, + { + "n": "N", + "v": "N" + }, + { + "n": "O", + "v": "O" + }, + { + "n": "P", + "v": "P" + }, + { + "n": "Q", + "v": "Q" + }, + { + "n": "R", + "v": "R" + }, + { + "n": "S", + "v": "S" + }, + { + "n": "T", + "v": "T" + }, + { + "n": "U", + "v": "U" + }, + { + "n": "V", + "v": "V" + }, + { + "n": "W", + "v": "W" + }, + { + "n": "X", + "v": "X" + }, + { + "n": "Y", + "v": "Y" + }, + { + "n": "Z", + "v": "Z" + }, + { + "n": "0-9", + "v": "0-9" + } + ] + }, + { + "key": "2", + "name": "排序", + "value": [ + { + "n": "时间", + "v": "time" + }, + { + "n": "人气", + "v": "hits" + }, + { + "n": "评分", + "v": "score" + } + ] + } + ], + "32": [ + { + "key": "5", + "name": "字母", + "value": [ + { + "n": "A", + "v": "A" + }, + { + "n": "B", + "v": "B" + }, + { + "n": "C", + "v": "C" + }, + { + "n": "D", + "v": "D" + }, + { + "n": "E", + "v": "E" + }, + { + "n": "F", + "v": "F" + }, + { + "n": "G", + "v": "G" + }, + { + "n": "H", + "v": "H" + }, + { + "n": "I", + "v": "I" + }, + { + "n": "J", + "v": "J" + }, + { + "n": "K", + "v": "K" + }, + { + "n": "L", + "v": "L" + }, + { + "n": "M", + "v": "M" + }, + { + "n": "N", + "v": "N" + }, + { + "n": "O", + "v": "O" + }, + { + "n": "P", + "v": "P" + }, + { + "n": "Q", + "v": "Q" + }, + { + "n": "R", + "v": "R" + }, + { + "n": "S", + "v": "S" + }, + { + "n": "T", + "v": "T" + }, + { + "n": "U", + "v": "U" + }, + { + "n": "V", + "v": "V" + }, + { + "n": "W", + "v": "W" + }, + { + "n": "X", + "v": "X" + }, + { + "n": "Y", + "v": "Y" + }, + { + "n": "Z", + "v": "Z" + }, + { + "n": "0-9", + "v": "0-9" + } + ] + }, + { + "key": "2", + "name": "排序", + "value": [ + { + "n": "时间", + "v": "time" + }, + { + "n": "人气", + "v": "hits" + }, + { + "n": "评分", + "v": "score" + } + ] + } + ] +} \ No newline at end of file