AndroidCatVodSpider/app/src/main/java/com/github/catvod/utils/ProxyVideo.java

238 lines
10 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.github.catvod.utils;
import android.os.SystemClock;
import android.text.TextUtils;
import com.github.catvod.crawler.SpiderDebug;
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.ArrayUtils;
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.*;
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 = 16;
public static String buildCommonProxyUrl(String url, Map<String, String> headers) {
return Proxy.getUrl() + "?do=proxy&url=" + Util.base64Encode(url.getBytes(Charset.defaultCharset())) + "&header=" + Util.base64Encode((new Gson().toJson(headers)).getBytes(Charset.defaultCharset()));
}
public static void go() {
boolean close = OkHttp.string(GO_SERVER).isEmpty();
if (close) OkHttp.string("http://127.0.0.1:" + Proxy.getPort() + "/go");
if (close) while (OkHttp.string(GO_SERVER).isEmpty()) SystemClock.sleep(20);
}
public static String goVer() {
try {
go();
String result = OkHttp.string(GO_SERVER + "version");
return new JSONObject(result).optString("version");
} catch (Exception e) {
return "";
}
}
public static String url(String url, int thread) {
if (!TextUtils.isEmpty(goVer()) && url.contains("/proxy?")) url += "&response=url";
return String.format(Locale.getDefault(), "%s?url=%s&thread=%d", GO_SERVER, URLEncoder.encode(url), thread);
}
public static Object[] proxy(String url, Map<String, String> headers) throws Exception {
SpiderDebug.log(" ++start proxy:");
SpiderDebug.log(" ++proxy url:" + url);
SpiderDebug.log(" ++proxy header:" + Json.toJson(headers));
Response response = OkHttp.newCall(url, headers);
SpiderDebug.log(" ++end proxy:");
SpiderDebug.log(" ++proxy res code:" + response.code());
SpiderDebug.log(" ++proxy res header:" + Json.toJson(response.headers()));
// SpiderDebug.log(" ++proxy res data:" + Json.toJson(response.body()));
String contentType = response.headers().get("Content-Type");
String contentDisposition = response.headers().get("Content-Disposition");
if (contentDisposition != null) contentType = getMimeType(contentDisposition);
Map<String, String> 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, response.body().byteStream(), respHeaders};
}
public static Object[] proxyMultiThread(String url, Map<String, String> headers) throws Exception {
SpiderDebug.log("--proxyMultiThread: start ");
Map<String, String> newHeaders = new HashMap<>(headers);
newHeaders.put("range", "bytes=0-0");
newHeaders.put("Range", "bytes=0-0");
Object[] info = proxy(url, newHeaders);
int code = (int) info[0];
if (code != 206) {
return proxy(url, headers);
}
String contentRange = StringUtils.isAllBlank(((Map<String, String>) info[3]).get("Content-Range")) ? ((Map<String, String>) info[3]).get("content-range") : ((Map<String, String>) info[3]).get("Content-Range");
SpiderDebug.log("--contentRange:" + contentRange);
//文件总大小
String total = StringUtils.split(contentRange, "/")[1];
SpiderDebug.log("--文件总大小:" + total);
String range = StringUtils.isAllBlank(headers.get("range")) ? headers.get("Range") : headers.get("range");
SpiderDebug.log("---proxyMultiThread,Range:" + range);
Map<String, String> rangeObj = parseRange(range);
//没有range,无需分割
if (rangeObj == null) {
SpiderDebug.log("没有range,无需分割");
return proxy(url, headers);
} else {
List<long[]> partList = generatePart(rangeObj, total);
ExecutorService service = Executors.newFixedThreadPool(THREAD_NUM);
// 存储执行结果的List
List<Future<Response>> results = new ArrayList<Future<Response>>();
for (long[] part : partList) {
String newRange = "bytes=" + part[0] + "-" + part[1];
SpiderDebug.log("下载开始" + ";newRange:" + newRange);
Map<String, String> headerNew = new HashMap<>(headers);
headerNew.put("range", newRange);
headerNew.put("Range", newRange);
Future<Response> result = service.submit(() -> {
try {
return OkHttp.newCall(url, headerNew);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
results.add(result);
}
byte[] bytes = null;
Response response = null;
for (int i = 0; i < THREAD_NUM; i++) {
// 获取包含返回结果的future对象
Future<Response> future = results.get(i);
// 从future中取出执行结果若尚未返回结果则get方法被阻塞直到结果被返回为止
response = future.get();
bytes = ArrayUtils.addAll(bytes, response.body().bytes());
SpiderDebug.log("---第" + i + "块下载完成" + ";Content-Range:" + response.headers().get("Content-Range"));
SpiderDebug.log("---第" + i + "块下载完成" + ";content-range:" + response.headers().get("content-range"));
}
service.shutdown();
String contentType = response.headers().get("Content-Type");
String contentDisposition = response.headers().get("Content-Disposition");
if (contentDisposition != null) contentType = getMimeType(contentDisposition);
Map<String, String> 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));
}
respHeaders.put("Content-Length", String.valueOf(bytes.length));
respHeaders.put("content-length", String.valueOf(bytes.length));
respHeaders.put("Content-Range", String.format("bytes %s-%s/%s", partList.get(0)[0], partList.get(THREAD_NUM - 1)[1], total));
respHeaders.put("content-range", String.format("bytes %s-%s/%s", partList.get(0)[0], partList.get(THREAD_NUM - 1)[1], total));
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 List<long[]> generatePart(Map<String, String> rangeObj, String total) {
long start = Long.parseLong(rangeObj.get("start"));
long end = StringUtils.isAllBlank(rangeObj.get("end")) ? start + 1024 * 1024 *8 : Long.parseLong(rangeObj.get("end"));
long totalSize = Long.parseLong(total);
end = Math.min(end, totalSize - 1);
long length = end - start + 1;
long size = length / THREAD_NUM;
List<long[]> partList = new ArrayList<>();
for (int i = 0; i < THREAD_NUM; i++) {
long partEnd = Math.min(start + size, end);
partList.add(new long[]{start, partEnd});
start = partEnd + 1;
}
return partList;
}
private static Map<String, String> parseRange(String range) {
SpiderDebug.log("parseRange:" + range);
if (StringUtils.isNoneBlank(range)) {
String[] ranges = StringUtils.split(range.replace("bytes=", ""), "-");
String start = ranges[0];
String end = ranges.length > 1 ? ranges[1] : "";
return Map.of("start", start, "end", end);
}
return null;
}
private static String getMimeType(String contentDisposition) {
if (contentDisposition.endsWith(".mp4")) {
return "video/mp4";
} else if (contentDisposition.endsWith(".webm")) {
return "video/webm";
} else if (contentDisposition.endsWith(".avi")) {
return "video/x-msvideo";
} else if (contentDisposition.endsWith(".wmv")) {
return "video/x-ms-wmv";
} else if (contentDisposition.endsWith(".flv")) {
return "video/x-flv";
} else if (contentDisposition.endsWith(".mov")) {
return "video/quicktime";
} else if (contentDisposition.endsWith(".mkv")) {
return "video/x-matroska";
} else if (contentDisposition.endsWith(".mpeg")) {
return "video/mpeg";
} else if (contentDisposition.endsWith(".3gp")) {
return "video/3gpp";
} else if (contentDisposition.endsWith(".ts")) {
return "video/MP2T";
} else if (contentDisposition.endsWith(".mp3")) {
return "audio/mp3";
} else if (contentDisposition.endsWith(".wav")) {
return "audio/wav";
} else if (contentDisposition.endsWith(".aac")) {
return "audio/aac";
} else {
return null;
}
}
/**
* 视频range
*/
}