diff --git a/app/build.gradle b/app/build.gradle index dd59cae7..da5bd8ba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,6 +11,7 @@ android { minSdk 17 targetSdk 29 ndk { abiFilters "armeabi-v7a" } + buildConfigField "String", "APP_ID", "\"5dde4e1bdf9e4966b387ba58f4b3fdc3\"" } buildTypes { @@ -35,6 +36,7 @@ android { dependencies { //Debug For HTTP/3 debugImplementation 'org.chromium.net:cronet-embedded:101.4951.41' + implementation 'com.starkbank.ellipticcurve:starkbank-ecdsa:1.0.2' implementation 'com.google.net.cronet:cronet-okhttp:0.1.0' implementation 'androidx.annotation:annotation:1.5.0' implementation 'com.squareup.okhttp3:okhttp:3.12.13' diff --git a/app/src/main/java/com/github/catvod/ali/API.java b/app/src/main/java/com/github/catvod/ali/API.java new file mode 100644 index 00000000..b8bb6cec --- /dev/null +++ b/app/src/main/java/com/github/catvod/ali/API.java @@ -0,0 +1,446 @@ +package com.github.catvod.ali; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.net.UrlQuerySanitizer; +import android.os.SystemClock; +import android.text.TextUtils; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.github.catvod.BuildConfig; +import com.github.catvod.bean.Sub; +import com.github.catvod.bean.Vod; +import com.github.catvod.bean.ali.Auth; +import com.github.catvod.bean.ali.Data; +import com.github.catvod.bean.ali.Item; +import com.github.catvod.net.OkHttp; +import com.github.catvod.spider.Init; +import com.github.catvod.spider.Proxy; +import com.github.catvod.utils.Prefers; +import com.github.catvod.utils.QRCode; +import com.github.catvod.utils.Trans; +import com.github.catvod.utils.Utils; +import com.starkbank.ellipticcurve.Ecdsa; +import com.starkbank.ellipticcurve.PrivateKey; +import com.starkbank.ellipticcurve.utils.BinaryAscii; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +public class API { + + private ScheduledExecutorService service; + private Map mediaId2Url; + private final ReentrantLock lock; + private AlertDialog dialog; + private final Auth auth; + + private static class Loader { + static volatile API INSTANCE = new API(); + } + + public static API get() { + return Loader.INSTANCE; + } + + private API() { + this.auth = new Auth(); + this.lock = new ReentrantLock(); + } + + public void setRefreshToken(String token) { + auth.setRefreshToken(Prefers.getString("token", token)); + } + + public void setShareId(String shareId) { + auth.setShareId(shareId); + refreshShareToken(); + } + + public HashMap getHeader() { + HashMap headers = new HashMap<>(); + headers.put("User-Agent", Utils.CHROME); + headers.put("Referer", "https://www.aliyundrive.com/"); + return headers; + } + + private HashMap getHeaderAuth() { + HashMap headers = getHeader(); + headers.put("authorization", auth.getAccessToken()); + headers.put("x-share-token", auth.getShareToken()); + return headers; + } + + private HashMap getHeaderSign() { + HashMap headers = getHeaderAuth(); + headers.put("x-device-id", auth.getDeviceId()); + headers.put("x-signature", auth.getSignature()); + return headers; + } + + private String post(String url, JSONObject body) { + url = url.startsWith("https") ? url : "https://api.aliyundrive.com/" + url; + return OkHttp.postJson(url, body.toString(), getHeader()); + } + + private String auth(String url, JSONObject body, boolean retry) { + url = url.startsWith("https") ? url : "https://api.aliyundrive.com/" + url; + String result = OkHttp.postJson(url, body.toString(), getHeaderAuth()); + if (retry && check401(result)) return auth(url, body, false); + return result; + } + + private String sign(String url, JSONObject body, boolean retry) { + url = url.startsWith("https") ? url : "https://api.aliyundrive.com/" + url; + String result = OkHttp.postJson(url, body.toString(), getHeaderSign()); + if (retry && check401(result)) return sign(url, body, false); + return result; + } + + private boolean check401(String result) { + if (result.contains("AccessTokenInvalid")) return refreshAccessToken(); + if (result.contains("ShareLinkTokenInvalid") || result.contains("InvalidParameterNotMatch")) return refreshShareToken(); + if (result.contains("UserDeviceOffline") || result.contains("UserDeviceIllegality") || result.contains("DeviceSessionSignatureInvalid")) return refreshSignature(); + return false; + } + + public void checkAccessToken() { + if (auth.getAccessToken().isEmpty()) refreshAccessToken(); + } + + private void checkSignature() { + if (auth.getSignature().isEmpty()) refreshSignature(); + } + + private boolean refreshAccessToken() { + try { + JSONObject body = new JSONObject(); + String token = auth.getRefreshToken(); + if (token.startsWith("http")) token = OkHttp.string(token).replaceAll("[^A-Za-z0-9]", ""); + body.put("refresh_token", token); + body.put("grant_type", "refresh_token"); + JSONObject object = new JSONObject(post("https://auth.aliyundrive.com/v2/account/token", body)); + auth.setUserId(object.getString("user_id")); + auth.setDeviceId(object.getString("device_id")); + auth.setAccessToken(object.getString("token_type") + " " + object.getString("access_token")); + auth.setRefreshToken(object.getString("refresh_token")); + return true; + } catch (Exception e) { + stopService(); + auth.clean(); + getQRCode(); + return true; + } finally { + while (auth.isEmpty()) SystemClock.sleep(250); + } + } + + public boolean refreshShareToken() { + try { + JSONObject body = new JSONObject(); + body.put("share_id", auth.getShareId()); + body.put("share_pwd", ""); + JSONObject object = new JSONObject(post("v2/share_link/get_share_token", body)); + auth.setShareToken(object.getString("share_token")); + return true; + } catch (Exception e) { + Init.show("來晚啦,該分享已失效。"); + e.printStackTrace(); + return false; + } + } + + private boolean refreshSignature() { + try { + PrivateKey privateKey = new PrivateKey(); + String pubKey = "04" + BinaryAscii.hexFromBinary(privateKey.publicKey().toByteString().getBytes()); + String message = BuildConfig.APP_ID + ":" + auth.getDeviceId() + ":" + auth.getUserId() + ":" + 0; + String signature = BinaryAscii.hexFromBinary(Ecdsa.sign(message, privateKey).toDer().getBytes()); + auth.setSignature(signature.substring(signature.length() - 128) + "01"); + JSONObject body = new JSONObject(); + body.put("deviceName", "samsung"); + body.put("modelName", "SM-G9810"); + body.put("nonce", 0); + body.put("pubKey", pubKey); + body.put("refreshToken", auth.getRefreshToken()); + JSONObject object = new JSONObject(sign("users/v1/users/device/create_session", body, false)); + if (!object.getBoolean("success")) throw new Exception(object.toString()); + return true; + } catch (Exception e) { + auth.setSignature(""); + e.printStackTrace(); + return false; + } + } + + public Vod getVod(String url, String fileId) throws Exception { + JSONObject body = new JSONObject(); + body.put("share_id", auth.getShareId()); + String json = API.get().post("adrive/v3/share_link/get_share_by_anonymous", body); + JSONObject object = new JSONObject(json); + List files = new ArrayList<>(); + LinkedHashMap> subMap = new LinkedHashMap<>(); + listFiles(new Item(getParentFileId(fileId, object)), files, subMap); + List playUrls = new ArrayList<>(); + for (Item file : files) playUrls.add(Trans.get(file.getDisplayName()) + "$" + file.getFileId() + findSubs(file.getName(), subMap)); + List sourceUrls = new ArrayList<>(); + sourceUrls.add(TextUtils.join("#", playUrls)); + sourceUrls.add(TextUtils.join("#", playUrls)); + Vod vod = new Vod(); + vod.setVodId(url); + vod.setVodContent(url); + vod.setVodPic(object.getString("avatar")); + vod.setVodName(object.getString("share_name")); + vod.setVodPlayUrl(TextUtils.join("$$$", sourceUrls)); + vod.setVodPlayFrom("原畫$$$普畫"); + vod.setTypeName("阿里雲盤"); + return vod; + } + + private void listFiles(Item folder, List files, LinkedHashMap> subMap) throws Exception { + listFiles(folder, files, subMap, ""); + } + + private void listFiles(Item parent, List files, LinkedHashMap> subMap, String marker) throws Exception { + JSONObject body = new JSONObject(); + List folders = new ArrayList<>(); + body.put("limit", 200); + body.put("share_id", auth.getShareId()); + 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(API.get().auth("adrive/v3/file/list", body, true)); + for (Item file : item.getItems()) { + if (file.getType().equals("folder")) { + folders.add(file); + } else if (file.getCategory().equals("video") || file.getCategory().equals("audio")) { + files.add(file.parent(parent.getName())); + } else if (Utils.isSub(file.getExt())) { + String key = file.removeExt(); + if (!subMap.containsKey(key)) subMap.put(key, new ArrayList<>()); + subMap.get(key).add(key + "@@@" + file.getExt() + "@@@" + file.getFileId()); + } + } + if (item.getNextMarker().length() > 0) { + listFiles(parent, files, subMap, item.getNextMarker()); + } + for (Item folder : folders) { + listFiles(folder, files, subMap); + } + } + + private String getParentFileId(String fileId, JSONObject shareInfo) throws Exception { + JSONArray array = shareInfo.getJSONArray("file_infos"); + 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 ""; + } + + private String findSubs(String name, Map> subMap) { + name = name.substring(0, name.lastIndexOf(".")); + List subs = subMap.get(name); + if (subs != null && subs.size() > 0) return combineSubs(subs); + StringBuilder sb = new StringBuilder(); + for (Map.Entry> entry : subMap.entrySet()) sb.append(combineSubs(entry.getValue())); + return sb.toString(); + } + + private String combineSubs(List subs) { + StringBuilder sb = new StringBuilder(); + for (String sub : subs) sb.append("+").append(sub); + return sb.toString(); + } + + public List getSub(String[] ids) { + List sub = new ArrayList<>(); + for (String text : ids) { + if (!text.contains("@@@")) continue; + String[] split = text.split("@@@"); + String name = split[0]; + String ext = split[1]; + String url = Proxy.getUrl() + "?do=ali&type=sub" + "&file_id=" + split[2]; + sub.add(Sub.create().name(name).ext(ext).url(url)); + } + return sub; + } + + public String getPreviewUrl(String fileId) { + return Proxy.getUrl() + "?do=ali&type=m3u8&file_id=" + fileId; + } + + public String getDownloadUrl(String fileId) { + try { + JSONObject body = new JSONObject(); + body.put("file_id", fileId); + body.put("share_id", auth.getShareId()); + body.put("expire_sec", 600); + String json = API.get().auth("v2/file/get_share_link_download_url", body, true); + String url = new JSONObject(json).optString("download_url"); + Map> respHeaders = new HashMap<>(); + OkHttp.stringNoRedirect(url, API.get().getHeader(), respHeaders); + return OkHttp.getRedirectLocation(respHeaders); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + + public Object[] proxySub(Map params) { + String fileId = params.get("file_id"); + String text = OkHttp.string(getDownloadUrl(fileId), API.get().getHeaderAuth()); + Object[] result = new Object[3]; + result[0] = 200; + result[1] = "application/octet-stream"; + result[2] = new ByteArrayInputStream(text.getBytes()); + return result; + } + + public Object[] proxyM3U8(Map params) { + String fileId = params.get("file_id"); + Object[] result = new Object[3]; + result[0] = 200; + result[1] = "application/vnd.apple.mpegurl"; + result[2] = new ByteArrayInputStream(refreshM3U8(fileId).getBytes()); + return result; + } + + public Object[] proxyMedia(Map params) { + try { + String fileId = params.get("file_id"); + String mediaId = params.get("media_id"); + lock.lock(); + String mediaUrl = mediaId2Url.get(mediaId); + long expires = Long.parseLong(new UrlQuerySanitizer(mediaUrl).getValue("x-oss-expires")); + long current = System.currentTimeMillis() / 1000; + if (expires - current <= 60) { + refreshM3U8(fileId); + mediaUrl = mediaId2Url.get(mediaId); + } + lock.unlock(); + Object[] result = new Object[3]; + result[0] = 200; + result[1] = "video/MP2T"; + result[2] = OkHttp.newCall(mediaUrl, getHeader()).body().byteStream(); + return result; + } catch (Exception e) { + return null; + } + } + + private String refreshM3U8(String fileId) { + try { + checkSignature(); + JSONObject body = new JSONObject(); + body.put("file_id", fileId); + body.put("share_id", auth.getShareId()); + body.put("template_id", ""); + body.put("category", "live_transcoding"); + String json = sign("v2/file/get_share_link_video_preview_play_info", body, true); + JSONArray taskList = new JSONObject(json).getJSONObject("video_preview_play_info").getJSONArray("live_transcoding_task_list"); + Map> respHeaders = new HashMap<>(); + OkHttp.stringNoRedirect(getPreviewQuality(taskList), getHeader(), respHeaders); + String location = OkHttp.getRedirectLocation(respHeaders); + String m3u8 = OkHttp.string(location, getHeader()); + String mediaUrlPrefix = location.substring(0, location.lastIndexOf("/")) + "/"; + List lines = new ArrayList<>(); + int mediaId = 0; + mediaId2Url = new HashMap<>(); + for (String line : m3u8.split("\n")) { + if (line.contains("x-oss-expires")) { + mediaId += 1; + mediaId2Url.put(String.valueOf(mediaId), mediaUrlPrefix + line); + line = Proxy.getUrl() + "?do=ali&type=media" + "&file_id=" + fileId + "&media_id=" + mediaId; + } + lines.add(line); + } + return TextUtils.join("\n", lines); + } catch (Exception e) { + return ""; + } + } + + private String getPreviewQuality(JSONArray taskList) throws Exception { + for (String templateId : Arrays.asList("FHD", "HD", "SD", "LD")) { + for (int i = 0; i < taskList.length(); ++i) { + JSONObject task = taskList.getJSONObject(i); + if (task.getString("template_id").equals(templateId)) { + return task.getString("url"); + } + } + } + return taskList.getJSONObject(0).getString("url"); + } + + private void getQRCode() { + Data data = Data.objectFrom(OkHttp.string("https://passport.aliyundrive.com/newlogin/qrcode/generate.do?appName=aliyun_drive&fromSite=52&appName=aliyun_drive&appEntrance=web&isMobile=false&lang=zh_CN&returnUrl=&bizParams=&_bx-v=2.2.3")).getContent().getData(); + Init.run(() -> showQRCode(data)); + } + + private void showQRCode(Data data) { + try { + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(Utils.dp2px(240), Utils.dp2px(240)); + ImageView image = new ImageView(Init.context()); + image.setScaleType(ImageView.ScaleType.CENTER_CROP); + image.setImageBitmap(QRCode.getBitmap(data.getCodeContent(), 240, 2)); + FrameLayout frame = new FrameLayout(Init.context()); + params.gravity = Gravity.CENTER; + frame.addView(image, params); + dialog = new AlertDialog.Builder(Init.getActivity()).setView(frame).setOnDismissListener(this::dismiss).show(); + dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + Init.execute(() -> startService(data.getParams())); + Init.show("請使用阿里雲盤 App 掃描二維碼"); + } catch (Exception ignored) { + } + } + + private void startService(Map params) { + service = Executors.newScheduledThreadPool(1); + service.scheduleAtFixedRate(() -> { + Data result = Data.objectFrom(OkHttp.post("https://passport.aliyundrive.com/newlogin/qrcode/query.do?appName=aliyun_drive&fromSite=52&_bx-v=2.2.3", params)).getContent().getData(); + if (result.hasToken()) setToken(result.getToken()); + }, 1, 1, TimeUnit.SECONDS); + } + + private void setToken(String value) { + Prefers.put("token", value); + Init.show("請重新進入播放頁"); + auth.setRefreshToken(value); + stopService(); + } + + private void stopService() { + if (service != null) service.shutdownNow(); + Init.run(this::dismiss); + } + + private void dismiss(DialogInterface dialog) { + stopService(); + } + + private void dismiss() { + try { + if (dialog != null) dialog.dismiss(); + } catch (Exception ignored) { + } + } +} diff --git a/app/src/main/java/com/github/catvod/bean/ali/Auth.java b/app/src/main/java/com/github/catvod/bean/ali/Auth.java index 997444fb..408ae0e4 100644 --- a/app/src/main/java/com/github/catvod/bean/ali/Auth.java +++ b/app/src/main/java/com/github/catvod/bean/ali/Auth.java @@ -37,7 +37,7 @@ public class Auth { } public String getSignature() { - return signature; + return TextUtils.isEmpty(signature) ? "" : signature; } public void setSignature(String signature) { diff --git a/app/src/main/java/com/github/catvod/net/OkHttp.java b/app/src/main/java/com/github/catvod/net/OkHttp.java index 45cd0c02..f4feeac0 100644 --- a/app/src/main/java/com/github/catvod/net/OkHttp.java +++ b/app/src/main/java/com/github/catvod/net/OkHttp.java @@ -2,13 +2,17 @@ package com.github.catvod.net; import com.github.catvod.crawler.Spider; +import java.io.IOException; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.Dns; +import okhttp3.Headers; import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; public class OkHttp { @@ -51,6 +55,10 @@ public class OkHttp { } } + public static Response newCall(String url, Map header) throws IOException { + return client().newCall(new Request.Builder().url(url).headers(Headers.of(header)).build()).execute(); + } + public static void stringNoRedirect(String url, Map header, Map> respHeader) { string(noRedirect(), GET, url, null, null, header, respHeader); } 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 f48ab8c3..7b447374 100644 --- a/app/src/main/java/com/github/catvod/spider/Ali.java +++ b/app/src/main/java/com/github/catvod/spider/Ali.java @@ -1,363 +1,55 @@ package com.github.catvod.spider; -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.os.SystemClock; -import android.text.TextUtils; -import android.util.Log; -import android.view.Gravity; -import android.widget.FrameLayout; -import android.widget.ImageView; +import android.content.Context; +import com.github.catvod.ali.API; import com.github.catvod.bean.Result; -import com.github.catvod.bean.Sub; -import com.github.catvod.bean.Vod; -import com.github.catvod.bean.ali.Auth; -import com.github.catvod.bean.ali.Data; -import com.github.catvod.bean.ali.Item; -import com.github.catvod.net.OkHttp; -import com.github.catvod.utils.Prefers; -import com.github.catvod.utils.QRCode; -import com.github.catvod.utils.Trans; -import com.github.catvod.utils.Utils; +import com.github.catvod.crawler.Spider; -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.ByteArrayInputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author ColaMint & Adam & FongMi */ -public class Ali { +public class Ali extends Spider { public static final Pattern pattern = Pattern.compile("www.aliyundrive.com/s/([^/]+)(/folder/([^/]+))?"); - private ScheduledExecutorService service; - private final Auth auth; - private AlertDialog dialog; - private static class Loader { - static volatile Ali INSTANCE = new Ali(); - } - - public static Ali get() { - return Loader.INSTANCE; - } - - public Ali() { - this.auth = new Auth(); - } - - public Ali init(String token) { - auth.setRefreshToken(Prefers.getString("token", token)); - return this; - } - - private HashMap getHeaders() { - HashMap headers = new HashMap<>(); - headers.put("User-Agent", Utils.CHROME); - headers.put("Referer", "https://www.aliyundrive.com/"); - return headers; - } - - private HashMap getAuthHeader() { - HashMap headers = getHeaders(); - headers.put("content-type", "application/json"); - headers.put("Authorization", auth.getAccessToken()); - headers.put("x-share-token", auth.getShareToken()); - return headers; - } - - private String post(String url, JSONObject body) { - url = url.startsWith("https") ? url : "https://api.aliyundrive.com/" + url; - return OkHttp.postJson(url, body.toString(), getHeaders()); - } - - private String postAuth(String url, JSONObject body) { - url = url.startsWith("https") ? url : "https://api.aliyundrive.com/" + url; - String result = OkHttp.postJson(url, body.toString(), getAuthHeader()); - if (check401(result)) return postAuth(url, body); - return result; - } - - private boolean check401(String result) { - if (result.contains("AccessTokenInvalid")) return refreshAccessToken(); - if (result.contains("ShareLinkTokenInvalid")) return refreshShareToken(); - if (result.contains("InvalidParameterNotMatch")) return refreshShareToken(); - return false; + @Override + public void init(Context context, String extend) { + API.get().setRefreshToken(extend); } + @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) : ""; - auth.setShareId(shareId); - refreshShareToken(); - return Result.string(getVod(url, fileId)); + API.get().setShareId(shareId); + return Result.string(API.get().getVod(url, fileId)); } - public String playerContent(String flag, String id) { + @Override + public String playerContent(String flag, String id, List vipFlags) { + API.get().checkAccessToken(); String[] ids = id.split("\\+"); - if (auth.isEmpty()) refreshAccessToken(); - return Result.get().url(getDownloadUrl(ids[0])).subs(getSub(ids)).header(getHeaders()).string(); - } - - private Vod getVod(String url, String fileId) throws Exception { - JSONObject body = new JSONObject(); - body.put("share_id", auth.getShareId()); - String json = post("adrive/v3/share_link/get_share_by_anonymous", body); - JSONObject object = new JSONObject(json); - List files = new ArrayList<>(); - LinkedHashMap> subMap = new LinkedHashMap<>(); - listFiles(new Item(getParentFileId(fileId, object)), files, subMap); - List playUrls = new ArrayList<>(); - for (Item file : files) playUrls.add(Trans.get(file.getDisplayName()) + "$" + file.getFileId() + findSubs(file.getName(), subMap)); - List sourceUrls = new ArrayList<>(); - sourceUrls.add(TextUtils.join("#", playUrls)); - sourceUrls.add(TextUtils.join("#", playUrls)); - Vod vod = new Vod(); - vod.setVodId(url); - vod.setVodContent(url); - vod.setVodPic(object.getString("avatar")); - vod.setVodName(object.getString("share_name")); - vod.setVodPlayUrl(TextUtils.join("$$$", sourceUrls)); - vod.setVodPlayFrom("原畫"); - vod.setTypeName("阿里雲盤"); - return vod; - } - - private void listFiles(Item folder, List files, LinkedHashMap> subMap) throws Exception { - listFiles(folder, files, subMap, ""); - } - - private void listFiles(Item parent, List files, LinkedHashMap> subMap, String marker) throws Exception { - JSONObject body = new JSONObject(); - List folders = new ArrayList<>(); - body.put("limit", 200); - body.put("share_id", auth.getShareId()); - 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(postAuth("adrive/v3/file/list", body)); - for (Item file : item.getItems()) { - if (file.getType().equals("folder")) { - folders.add(file); - } else if (file.getCategory().equals("video") || file.getCategory().equals("audio")) { - files.add(file.parent(parent.getName())); - } else if (Utils.isSub(file.getExt())) { - String key = file.removeExt(); - if (!subMap.containsKey(key)) subMap.put(key, new ArrayList<>()); - subMap.get(key).add(key + "@@@" + file.getExt() + "@@@" + file.getFileId()); - } - } - if (item.getNextMarker().length() > 0) { - listFiles(parent, files, subMap, item.getNextMarker()); - } - for (Item folder : folders) { - listFiles(folder, files, subMap); + if (flag.equals("原畫")) { + return Result.get().url(API.get().getDownloadUrl(ids[0])).subs(API.get().getSub(ids)).header(API.get().getHeader()).parse(0).string(); + } else { + return Result.get().url(API.get().getPreviewUrl(ids[0])).subs(API.get().getSub(ids)).header(API.get().getHeader()).parse(0).string(); } } - private String getParentFileId(String fileId, JSONObject shareInfo) throws Exception { - JSONArray array = shareInfo.getJSONArray("file_infos"); - 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 ""; - } - - private boolean refreshAccessToken() { - try { - JSONObject body = new JSONObject(); - String token = auth.getRefreshToken(); - if (token.startsWith("http")) token = OkHttp.string(token).replaceAll("[^A-Za-z0-9]", ""); - body.put("refresh_token", token); - body.put("grant_type", "refresh_token"); - JSONObject object = new JSONObject(post("https://auth.aliyundrive.com/v2/account/token", body)); - auth.setUserId(object.getString("user_id")); - auth.setDeviceId(object.getString("device_id")); - auth.setAccessToken(object.getString("token_type") + " " + object.getString("access_token")); - auth.setRefreshToken(object.getString("refresh_token")); - return true; - } catch (Exception e) { - stopService(); - auth.clean(); - getQRCode(); - return true; - } finally { - while (auth.isEmpty()) SystemClock.sleep(250); - } - } - - private boolean refreshShareToken() { - try { - JSONObject body = new JSONObject(); - body.put("share_id", auth.getShareId()); - body.put("share_pwd", ""); - JSONObject object = new JSONObject(post("v2/share_link/get_share_token", body)); - auth.setShareToken(object.getString("share_token")); - return true; - } catch (Exception e) { - Init.show("來晚啦,該分享已失效。"); - e.printStackTrace(); - return false; - } - } - - private String findSubs(String name, Map> subMap) { - name = name.substring(0, name.lastIndexOf(".")); - List subs = subMap.get(name); - if (subs != null && subs.size() > 0) return combineSubs(subs); - StringBuilder sb = new StringBuilder(); - for (Map.Entry> entry : subMap.entrySet()) sb.append(combineSubs(entry.getValue())); - return sb.toString(); - } - - private String combineSubs(List subs) { - StringBuilder sb = new StringBuilder(); - for (String sub : subs) sb.append("+").append(sub); - return sb.toString(); - } - - private List getSub(String[] ids) { - List sub = new ArrayList<>(); - for (String text : ids) { - if (!text.contains("@@@")) continue; - String[] split = text.split("@@@"); - String name = split[0]; - String ext = split[1]; - String url = Proxy.getUrl() + "?do=ali&type=sub" + "&file_id=" + split[2]; - sub.add(Sub.create().name(name).ext(ext).url(url)); - } - return sub; - } - - private String getPreviewQuality(JSONArray taskList) throws Exception { - for (String templateId : Arrays.asList("FHD", "HD", "SD", "LD")) { - for (int i = 0; i < taskList.length(); ++i) { - JSONObject task = taskList.getJSONObject(i); - if (task.getString("template_id").equals(templateId)) { - return task.getString("url"); - } - } - } - return taskList.getJSONObject(0).getString("url"); - } - - private String getPreviewUrl(String fileId) { - try { - JSONObject body = new JSONObject(); - body.put("file_id", fileId); - body.put("share_id", auth.getShareId()); - body.put("template_id", ""); - body.put("category", "live_transcoding"); - String json = postAuth("v2/file/get_share_link_video_preview_play_info", body); - JSONArray taskList = new JSONObject(json).getJSONObject("video_preview_play_info").getJSONArray("live_transcoding_task_list"); - Map> respHeaders = new HashMap<>(); - OkHttp.stringNoRedirect(getPreviewQuality(taskList), getHeaders(), respHeaders); - return OkHttp.getRedirectLocation(respHeaders); - } catch (Exception e) { - e.printStackTrace(); - return ""; - } - } - - private String getDownloadUrl(String fileId) { - try { - JSONObject body = new JSONObject(); - body.put("file_id", fileId); - body.put("share_id", auth.getShareId()); - body.put("expire_sec", 600); - String json = postAuth("v2/file/get_share_link_download_url", body); - Log.e("DDD", json); - String url = new JSONObject(json).optString("download_url"); - Map> respHeaders = new HashMap<>(); - OkHttp.stringNoRedirect(url, getHeaders(), respHeaders); - return OkHttp.getRedirectLocation(respHeaders); - } catch (Exception e) { - e.printStackTrace(); - return ""; - } - } - - public Object[] vod(Map params) { - String fileId = params.get("file_id"); - String text = OkHttp.string(getDownloadUrl(fileId), getAuthHeader()); - Object[] result = new Object[3]; - result[0] = 200; - result[1] = "application/octet-stream"; - result[2] = new ByteArrayInputStream(text.getBytes()); - return result; - } - - private void getQRCode() { - Data data = Data.objectFrom(OkHttp.string("https://passport.aliyundrive.com/newlogin/qrcode/generate.do?appName=aliyun_drive&fromSite=52&appName=aliyun_drive&appEntrance=web&isMobile=false&lang=zh_CN&returnUrl=&bizParams=&_bx-v=2.2.3")).getContent().getData(); - Init.run(() -> showQRCode(data)); - } - - private void showQRCode(Data data) { - try { - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(Utils.dp2px(240), Utils.dp2px(240)); - ImageView image = new ImageView(Init.context()); - image.setScaleType(ImageView.ScaleType.CENTER_CROP); - image.setImageBitmap(QRCode.getBitmap(data.getCodeContent(), 240, 2)); - FrameLayout frame = new FrameLayout(Init.context()); - params.gravity = Gravity.CENTER; - frame.addView(image, params); - dialog = new AlertDialog.Builder(Init.getActivity()).setView(frame).setOnDismissListener(this::dismiss).show(); - dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); - Init.execute(() -> startService(data.getParams())); - Init.show("請使用阿里雲盤 App 掃描二維碼"); - } catch (Exception ignored) { - } - } - - private void startService(Map params) { - service = Executors.newScheduledThreadPool(1); - service.scheduleAtFixedRate(() -> { - Data result = Data.objectFrom(OkHttp.post("https://passport.aliyundrive.com/newlogin/qrcode/query.do?appName=aliyun_drive&fromSite=52&_bx-v=2.2.3", params)).getContent().getData(); - if (result.hasToken()) setToken(result.getToken()); - }, 1, 1, TimeUnit.SECONDS); - } - - private void setToken(String value) { - Prefers.put("token", value); - Init.show("請重新進入播放頁"); - auth.setRefreshToken(value); - stopService(); - } - - private void stopService() { - if (service != null) service.shutdownNow(); - Init.run(this::dismiss); - } - - private void dismiss(DialogInterface dialog) { - stopService(); - } - - private void dismiss() { - try { - if (dialog != null) dialog.dismiss(); - } catch (Exception ignored) { - } + public static Object[] vod(Map params) { + String type = params.get("type"); + if (type.equals("sub")) return API.get().proxySub(params); + if (type.equals("m3u8")) return API.get().proxyM3U8(params); + if (type.equals("media")) return API.get().proxyMedia(params); + return null; } } \ No newline at end of file diff --git a/app/src/main/java/com/github/catvod/spider/PanSou.java b/app/src/main/java/com/github/catvod/spider/PanSou.java index 6d147114..f99398ad 100644 --- a/app/src/main/java/com/github/catvod/spider/PanSou.java +++ b/app/src/main/java/com/github/catvod/spider/PanSou.java @@ -1,10 +1,7 @@ package com.github.catvod.spider; -import android.content.Context; - import com.github.catvod.bean.Result; import com.github.catvod.bean.Vod; -import com.github.catvod.crawler.Spider; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Utils; @@ -22,7 +19,7 @@ import java.util.Map; /** * @author ColaMint & FongMi */ -public class PanSou extends Spider { +public class PanSou extends Ali { private final String siteUrl = "https://www.alipansou.com"; @@ -34,19 +31,14 @@ public class PanSou extends Spider { return headers; } - @Override - public void init(Context context, String extend) { - Ali.get().init(extend); - } - @Override public String detailContent(List ids) throws Exception { - if (Ali.pattern.matcher(ids.get(0)).find()) return Ali.get().detailContent(ids); + if (pattern.matcher(ids.get(0)).find()) return super.detailContent(ids); String url = siteUrl + ids.get(0).replace("/s/", "/cv/"); Map> respHeaders = new HashMap<>(); OkHttp.stringNoRedirect(url, getHeaders(ids.get(0)), respHeaders); url = OkHttp.getRedirectLocation(respHeaders); - return Ali.get().detailContent(Arrays.asList(url)); + return super.detailContent(Arrays.asList(url)); } @Override @@ -72,9 +64,4 @@ public class PanSou extends Spider { } return Result.string(list); } - - @Override - public String playerContent(String flag, String id, List vipFlags) { - return Ali.get().playerContent(flag, id); - } } diff --git a/app/src/main/java/com/github/catvod/spider/Paper.java b/app/src/main/java/com/github/catvod/spider/Paper.java index 8719738e..95523dfd 100644 --- a/app/src/main/java/com/github/catvod/spider/Paper.java +++ b/app/src/main/java/com/github/catvod/spider/Paper.java @@ -7,7 +7,6 @@ import com.github.catvod.bean.Filter; import com.github.catvod.bean.Result; import com.github.catvod.bean.Vod; import com.github.catvod.bean.paper.Data; -import com.github.catvod.crawler.Spider; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Utils; @@ -27,7 +26,7 @@ import java.util.Map; /** * @author ColaMint & FongMi */ -public class Paper extends Spider { +public class Paper extends Ali { private final String url = "https://gitcafe.net/alipaper/"; private final String api = "https://gitcafe.net/tool/alipaper/"; @@ -41,8 +40,8 @@ public class Paper extends Spider { @Override public void init(Context context, String extend) { + super.init(context, extend); types = Arrays.asList("hyds", "rhds", "omds", "qtds", "hydy", "rhdy", "omdy", "qtdy", "hydm", "rhdm", "omdm", "jlp", "zyp", "jypx", "qtsp"); - Ali.get().init(extend); } @Override @@ -90,11 +89,6 @@ public class Paper extends Spider { return Result.string(list); } - @Override - public String detailContent(List ids) throws Exception { - return Ali.get().detailContent(ids); - } - @Override public String searchContent(String key, boolean quick) { List list = new ArrayList<>(); @@ -105,9 +99,4 @@ public class Paper extends Spider { for (Data item : Data.arrayFrom(result)) if (types.contains(item.getCat()) && item.getTitle().contains(key)) list.add(item.getVod()); return Result.string(list); } - - @Override - public String playerContent(String flag, String id, List vipFlags) throws Exception { - return Ali.get().playerContent(flag, id); - } } diff --git a/app/src/main/java/com/github/catvod/spider/Proxy.java b/app/src/main/java/com/github/catvod/spider/Proxy.java index 1fc399c8..123b639a 100644 --- a/app/src/main/java/com/github/catvod/spider/Proxy.java +++ b/app/src/main/java/com/github/catvod/spider/Proxy.java @@ -18,7 +18,7 @@ public class Proxy extends Spider { case "ck": return new Object[]{200, "text/plain; charset=utf-8", new ByteArrayInputStream("ok".getBytes("UTF-8"))}; case "ali": - return Ali.get().vod(params); + return Ali.vod(params); default: return null; } diff --git a/app/src/main/java/com/github/catvod/spider/Push.java b/app/src/main/java/com/github/catvod/spider/Push.java index a6b5235b..d978a4a5 100644 --- a/app/src/main/java/com/github/catvod/spider/Push.java +++ b/app/src/main/java/com/github/catvod/spider/Push.java @@ -1,26 +1,19 @@ package com.github.catvod.spider; -import android.content.Context; import android.text.TextUtils; import com.github.catvod.bean.Result; import com.github.catvod.bean.Vod; -import com.github.catvod.crawler.Spider; import java.util.Arrays; import java.util.List; -public class Push extends Spider { - - @Override - public void init(Context context, String extend) { - Ali.get().init(extend); - } +public class Push extends Ali { @Override public String detailContent(List ids) throws Exception { String url = ids.get(0).trim(); - if (url.contains("aliyundrive")) return Ali.get().detailContent(ids); + if (url.contains("aliyundrive")) return super.detailContent(ids); List playFrom = Arrays.asList("直連", "嗅探", "解析"); List playUrl = Arrays.asList("播放$" + url, "播放$" + url, "播放$" + url); Vod vod = create(url); @@ -31,7 +24,7 @@ public class Push extends Spider { @Override public String playerContent(String flag, String id, List vipFlags) { - if (flag.contains("畫")) return Ali.get().playerContent(flag, id); + if (flag.contains("畫")) return super.playerContent(flag, id, vipFlags); if (flag.equals("嗅探")) return Result.get().parse().url(id).string(); if (flag.equals("解析")) return Result.get().parse().jx().url(id).string(); return Result.get().url(id).string(); diff --git a/app/src/main/java/com/github/catvod/spider/UpYun.java b/app/src/main/java/com/github/catvod/spider/UpYun.java index c920912f..ab66d115 100644 --- a/app/src/main/java/com/github/catvod/spider/UpYun.java +++ b/app/src/main/java/com/github/catvod/spider/UpYun.java @@ -1,24 +1,21 @@ package com.github.catvod.spider; -import android.content.Context; import android.util.Base64; import com.github.catvod.bean.Result; import com.github.catvod.bean.Vod; import com.github.catvod.bean.upyun.Data; import com.github.catvod.bean.upyun.Item; -import com.github.catvod.crawler.Spider; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Utils; import java.net.URLEncoder; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -public class UpYun extends Spider { +public class UpYun extends Ali { private final String siteUrl = "https://www.upyunso.com/"; private final String apiUrl = "https://api.upyunso2.com/"; @@ -30,16 +27,6 @@ public class UpYun extends Spider { return headers; } - @Override - public void init(Context context, String extend) { - Ali.get().init(extend); - } - - @Override - public String detailContent(List ids) throws Exception { - return Ali.get().detailContent(Arrays.asList(ids.get(0))); - } - @Override public String searchContent(String key, boolean quick) { String url = apiUrl + "search?keyword=" + URLEncoder.encode(key) + "&page=1&s_type=2"; @@ -48,9 +35,4 @@ public class UpYun extends Spider { for (Item item : Data.objectFrom(res).getResult().getItems()) if (item.isAli() && item.getTitle().contains(key)) list.add(item.getVod()); return Result.string(list); } - - @Override - public String playerContent(String flag, String id, List vipFlags) { - return Ali.get().playerContent(flag, id); - } } \ No newline at end of file diff --git a/app/src/main/java/com/github/catvod/spider/YiSo.java b/app/src/main/java/com/github/catvod/spider/YiSo.java index d77388a9..e1a47d7a 100644 --- a/app/src/main/java/com/github/catvod/spider/YiSo.java +++ b/app/src/main/java/com/github/catvod/spider/YiSo.java @@ -1,6 +1,5 @@ package com.github.catvod.spider; -import android.content.Context; import android.os.Build; import android.os.SystemClock; import android.webkit.WebView; @@ -8,32 +7,15 @@ import android.webkit.WebViewClient; import com.github.catvod.bean.Result; import com.github.catvod.bean.yiso.Item; -import com.github.catvod.crawler.Spider; import com.github.catvod.utils.Utils; import com.google.gson.JsonParser; import java.net.URLEncoder; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Objects; -public class YiSo extends Spider { - - @Override - public void init(Context context, String extend) { - Ali.get().init(extend); - } - - @Override - public String detailContent(List ids) throws Exception { - return Ali.get().detailContent(ids); - } - - @Override - public String playerContent(String flag, String id, List vipFlags) throws Exception { - return Ali.get().playerContent(flag, id); - } +public class YiSo extends Ali { @Override public String searchContent(String key, boolean quick) { diff --git a/app/src/main/java/com/github/catvod/spider/Zhaozy.java b/app/src/main/java/com/github/catvod/spider/Zhaozy.java index 81201eed..b286cee3 100644 --- a/app/src/main/java/com/github/catvod/spider/Zhaozy.java +++ b/app/src/main/java/com/github/catvod/spider/Zhaozy.java @@ -4,7 +4,6 @@ import android.content.Context; import com.github.catvod.bean.Result; import com.github.catvod.bean.Vod; -import com.github.catvod.crawler.Spider; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Utils; @@ -21,7 +20,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class Zhaozy extends Spider { +public class Zhaozy extends Ali { private final Pattern regexAli = Pattern.compile("(https://www.aliyundrive.com/s/[^\"]+)"); private final Pattern regexVid = Pattern.compile("(\\S+)"); @@ -55,24 +54,19 @@ public class Zhaozy extends Spider { @Override public void init(Context context, String extend) { String[] split = extend.split("\\$\\$\\$"); - Ali.get().init(split[0]); + super.init(context, split[0]); username = split[1]; password = split[2]; } @Override public String detailContent(List ids) throws Exception { - if (Ali.pattern.matcher(ids.get(0)).find()) return Ali.get().detailContent(ids); + if (pattern.matcher(ids.get(0)).find()) return super.detailContent(ids); Matcher matcher = regexAli.matcher(OkHttp.string(siteUrl + ids.get(0), getHeader())); - if (matcher.find()) return Ali.get().detailContent(Arrays.asList(matcher.group(1))); + if (matcher.find()) return super.detailContent(Arrays.asList(matcher.group(1))); return ""; } - @Override - public String playerContent(String flag, String id, List vipFlags) throws Exception { - return Ali.get().playerContent(flag, id); - } - @Override public String searchContent(String key, boolean quick) throws Exception { String url = siteUrl + "so?filename=" + URLEncoder.encode(key); diff --git a/jar/custom_spider.jar b/jar/custom_spider.jar index 75fa2c49..a488dc64 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 e72926a7..3389ec87 100644 --- a/jar/custom_spider.jar.md5 +++ b/jar/custom_spider.jar.md5 @@ -1 +1 @@ -029605ba89c58f14982a8aa1665cbeef +798306224186a353374b7bbaed582bde diff --git a/json/adult.json b/json/adult.json index d69229e9..d2800791 100644 --- a/json/adult.json +++ b/json/adult.json @@ -1,5 +1,5 @@ { - "spider": "https://gh-proxy.com/https://raw.githubusercontent.com/FongMi/CatVodSpider/main/jar/custom_spider.jar;md5;029605ba89c58f14982a8aa1665cbeef", + "spider": "https://gh-proxy.com/https://raw.githubusercontent.com/FongMi/CatVodSpider/main/jar/custom_spider.jar;md5;798306224186a353374b7bbaed582bde", "wallpaper": "https://gao.chuqiuyu.tk", "lives": [ { diff --git a/json/config.json b/json/config.json index dfa7a424..d6f0c8c5 100644 --- a/json/config.json +++ b/json/config.json @@ -1,5 +1,5 @@ { - "spider": "https://gh-proxy.com/https://raw.githubusercontent.com/FongMi/CatVodSpider/main/jar/custom_spider.jar;md5;029605ba89c58f14982a8aa1665cbeef", + "spider": "https://gh-proxy.com/https://raw.githubusercontent.com/FongMi/CatVodSpider/main/jar/custom_spider.jar;md5;798306224186a353374b7bbaed582bde", "wallpaper": "http://www.kf666888.cn/api/tvbox/img", "lives": [ {