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 5dcff262..83ffe3e3 100644 --- a/app/src/main/java/com/github/catvod/api/AliYun.java +++ b/app/src/main/java/com/github/catvod/api/AliYun.java @@ -507,7 +507,7 @@ public class AliYun { Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); List keys = Arrays.asList("referer", "icy-metadata", "range", "connection", "accept-encoding", "user-agent"); for (String key : params.keySet()) if (keys.contains(key)) headers.put(key, params.get(key)); - return ProxyVideo.proxy(downloadUrl, headers); + return ProxyVideo.proxyMultiThread(downloadUrl, headers); } private String getM3u8Url(String shareId, String fileId, String templateId) { diff --git a/app/src/main/java/com/github/catvod/api/QuarkApi.java b/app/src/main/java/com/github/catvod/api/QuarkApi.java index 2729557f..c0aaa1c0 100644 --- a/app/src/main/java/com/github/catvod/api/QuarkApi.java +++ b/app/src/main/java/com/github/catvod/api/QuarkApi.java @@ -73,7 +73,7 @@ public class QuarkApi { if (Util.getExt(url).contains("m3u8")) { return getM3u8(url, header); } - return ProxyVideo.proxy(url, header); + return ProxyVideo.proxyMultiThread(url, header); } /** diff --git a/app/src/main/java/com/github/catvod/utils/ProxyVideo.java b/app/src/main/java/com/github/catvod/utils/ProxyVideo.java index af4b372f..999012a2 100644 --- a/app/src/main/java/com/github/catvod/utils/ProxyVideo.java +++ b/app/src/main/java/com/github/catvod/utils/ProxyVideo.java @@ -7,17 +7,23 @@ import com.github.catvod.net.OkHttp; import com.github.catvod.spider.Proxy; import com.google.gson.Gson; import okhttp3.Response; +import org.apache.commons.lang3.StringUtils; import org.json.JSONObject; +import java.io.ByteArrayInputStream; import java.net.URLEncoder; import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; public class ProxyVideo { private static final String GO_SERVER = "http://127.0.0.1:7777/"; + //线程数4 + private static final int THREAD_NUM = 4; + public static String buildCommonProxyUrl(String url, Map headers) { return Proxy.getUrl() + "?do=proxy&url=" + Util.base64Encode(url.getBytes(Charset.defaultCharset())) + "&header=" + Util.base64Encode((new Gson().toJson(headers)).getBytes(Charset.defaultCharset())); @@ -70,6 +76,84 @@ public class ProxyVideo { return new Object[]{response.code(), contentType, response.body().byteStream(), respHeaders}; } + + public static Object[] proxyMultiThread(String url, Map headers) throws Exception { + + String range = headers.get("Range"); + Range rangeObj = parseRange(range); + //没有range,无需分割 + if (rangeObj == null) { + return proxy(url, headers); + } else { + //end 为空,测试请求 + if (StringUtils.isAllBlank(rangeObj.getEnd())) { + return proxy(url, headers); + } else { + long start = Long.parseLong(rangeObj.getStart()); + long end = Long.parseLong(rangeObj.getEnd()); + + long size = end - start; + //每块大小 + long partSize = size / THREAD_NUM; + ExecutorService service = Executors.newFixedThreadPool(THREAD_NUM); +// 存储执行结果的List + List> results = new ArrayList>(); + for (int i = 0; i < THREAD_NUM; i++) { + long partEnd = start + partSize >= end ? end : start + partSize; + String newRange = "range=" + start + "-" + partEnd; + start = partEnd; + + headers.put("Range", newRange); + Future result = service.submit(() -> { + try { + return OkHttp.newCall(url, headers); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + results.add(result); + } + byte[] bytes = new byte[(int) size]; + Response response = null; + for (int i = 0; i < THREAD_NUM; i++) { + // 获取包含返回结果的future对象 + Future future = results.get(i); + // 从future中取出执行结果(若尚未返回结果,则get方法被阻塞,直到结果被返回为止) + response = future.get(); + response.body().byteStream().read(bytes, (int) (i * partSize), (int) partSize); + SpiderDebug.log("---第" + i + "块下载完成" + ";headers:" + Json.toJson(response.headers())); + + } + String contentType = response.headers().get("Content-Type"); + String contentDisposition = response.headers().get("Content-Disposition"); + if (contentDisposition != null) contentType = getMimeType(contentDisposition); + Map respHeaders = new HashMap<>(); + /* respHeaders.put("Access-Control-Allow-Credentials", "true"); + respHeaders.put("Access-Control-Allow-Origin", "*");*/ + + for (String key : response.headers().names()) { + respHeaders.put(key, response.headers().get(key)); + } + SpiderDebug.log("++proxy res contentType:" + contentType); + // SpiderDebug.log("++proxy res body:" + response.body()); + SpiderDebug.log("++proxy res respHeaders:" + Json.toJson(respHeaders)); + return new Object[]{response.code(), contentType, new ByteArrayInputStream(bytes), respHeaders}; + + } + } + + } + + private static Range parseRange(String range) { + if (StringUtils.isNoneBlank(range)) { + String[] ranges = range.replace("bytes=", "").split("-"); + String start = ranges[0]; + String end = ranges[1]; + return new Range(start, end); + } + return null; + } + private static String getMimeType(String contentDisposition) { if (contentDisposition.endsWith(".mp4")) { return "video/mp4"; @@ -101,4 +185,33 @@ public class ProxyVideo { return null; } } + + /** + * 视频range + */ + private static class Range { + private String start; + private String end; + + public Range(String start, String end) { + start = start; + end = end; + } + + public String getStart() { + return start; + } + + public void setStart(String start) { + this.start = start; + } + + public String getEnd() { + return end; + } + + public void setEnd(String end) { + this.end = end; + } + } } diff --git a/jar/custom_spider.jar b/jar/custom_spider.jar index 393908e4..e68dfaca 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 e6718f60..672c5b4f 100644 --- a/jar/custom_spider.jar.md5 +++ b/jar/custom_spider.jar.md5 @@ -1 +1 @@ -4a9814b359e0f39cf4613cd801871c06 +e815d386e2777a1c728d0133698de99a diff --git a/json/test.json b/json/test.json index 0dc15213..6904b4e5 100644 --- a/json/test.json +++ b/json/test.json @@ -1,5 +1,5 @@ { - "spider": "https://ghproxy.net/https://raw.githubusercontent.com/lushunming/AndroidCatVodSpider/refs/heads/tianyiPassword/jar/custom_spider.jar;md5;bb54c03fd035f011b948b4ccf1273819", + "spider": "https://ghproxy.net/https://raw.githubusercontent.com/lushunming/AndroidCatVodSpider/refs/heads/multiThread/jar/custom_spider.jar;md5;e815d386e2777a1c728d0133698de99a", "lives": [ { "name": "电视直播", diff --git a/settings.gradle b/settings.gradle index cbe9798e..72fd0782 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,6 @@ pluginManagement { repositories { - mavenCentral() + maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' } @@ -9,6 +9,7 @@ pluginManagement { maven { url 'https://maven.aliyun.com/repository/mapr-public/' } gradlePluginPortal() google() + mavenCentral() maven { url "https://plugins.gradle.org/m2/" } maven { url "https://www.jitpack.io" } @@ -17,7 +18,7 @@ pluginManagement { dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { - mavenCentral() + maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' } @@ -25,6 +26,7 @@ dependencyResolutionManagement { maven { url 'https://maven.aliyun.com/repository/central/'} maven { url 'https://maven.aliyun.com/repository/mapr-public/' } google() + mavenCentral() maven { url "https://www.jitpack.io" } } }