This commit is contained in:
qlql765 2024-12-13 13:04:32 +08:00
parent f7f5dc2409
commit 513326e562
6 changed files with 494 additions and 4 deletions

View File

@ -0,0 +1,155 @@
package com.github.catvod.bean.uvod;
import android.text.TextUtils;
import com.github.catvod.bean.Vod;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.annotations.SerializedName;
import java.util.Collections;
import java.util.List;
public class Data {
@SerializedName(value = "video_latest_list", alternate = {"video_list"})
private List<Videolatest> videolatestlist;
@SerializedName(value = "video", alternate = {"video_soruce"})
private Video video;
@SerializedName("video_fragment_list")
private List<VideoFragmentList> videoFragmentList;
public static Data objectFrom(String str) {
JsonObject jsonObject = JsonParser.parseString(str).getAsJsonObject();
if (jsonObject.has("data")) {
return new Gson().fromJson(jsonObject.get("data"), Data.class);
}
return new Data();
}
public List<Videolatest> getVideolatest() {
return videolatestlist == null ? Collections.emptyList() : videolatestlist;
}
public Video getVideo() {
return video == null ? new Video() : video;
}
public List<VideoFragmentList> getVideoFragmentList() {
return videoFragmentList == null ? Collections.emptyList() : videoFragmentList;
}
public static class Videolatest {
@SerializedName("id")
private String id;
@SerializedName("title")
private String title;
@SerializedName("pic")
private String pic;
@SerializedName("state")
private String state;
@SerializedName("last_fragment_symbol")
private String lastfragment;
@SerializedName("year")
private String year;
public String getId() {
return TextUtils.isEmpty(id) ? "" : id;
}
public String getTitle() {
return TextUtils.isEmpty(title) ? "" : title;
}
public String getPic() {
return TextUtils.isEmpty(pic) ? "" : pic;
}
public String getState() {
return TextUtils.isEmpty(state) ? "" : state;
}
public String getLastFragment() {
return TextUtils.isEmpty(lastfragment) ? "" : lastfragment;
}
public String getYear() {
return TextUtils.isEmpty(year) ? "" : year;
}
public Vod vod() {
return new Vod(getId(), getTitle(), getPic(), getState() + getLastFragment());
}
}
public static class Video {
@SerializedName("year")
private String year;
@SerializedName("region")
private String region;
@SerializedName("starring")
private String starring;
@SerializedName("state")
private String state;
@SerializedName("description")
private String description;
@SerializedName("director")
private String director;
@SerializedName("language")
private String language;
@SerializedName("url")
private String url;
public String getYear() {
return TextUtils.isEmpty(year) ? "" : year;
}
public String getRegion() {
return TextUtils.isEmpty(region) ? "" : region;
}
public String getStarring() {
return TextUtils.isEmpty(starring) ? "" : starring;
}
public String getDescription() {
return TextUtils.isEmpty(description) ? "" : description;
}
public String getState() {
return TextUtils.isEmpty(state) ? "" : state;
}
public String getDirector() {
return TextUtils.isEmpty(director) ? "" : director;
}
public String getLanguage() {
return TextUtils.isEmpty(language) ? "" : language;
}
public String getUrl() {
return TextUtils.isEmpty(url) ? "" : url;
}
}
public static class VideoFragmentList {
@SerializedName("id")
private String id;
@SerializedName("symbol")
private String symbol;
public String getId() {
return TextUtils.isEmpty(id) ? "" : id;
}
public String getSymbol() {
return TextUtils.isEmpty(symbol) ? "" : symbol;
}
}
}

View File

@ -7,7 +7,7 @@ import android.widget.Button;
import com.github.catvod.R; import com.github.catvod.R;
import com.github.catvod.crawler.Spider; import com.github.catvod.crawler.Spider;
import com.github.catvod.spider.Init; import com.github.catvod.spider.Init;
import com.github.catvod.spider.Kanqiu; import com.github.catvod.spider.Uvod;
import com.orhanobut.logger.AndroidLogAdapter; import com.orhanobut.logger.AndroidLogAdapter;
import com.orhanobut.logger.Logger; import com.orhanobut.logger.Logger;
@ -46,7 +46,7 @@ public class MainActivity extends Activity {
private void initSpider() { private void initSpider() {
try { try {
Init.init(getApplicationContext()); Init.init(getApplicationContext());
spider = new Kanqiu(); spider = new Uvod();
spider.init(this, ""); spider.init(this, "");
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@ -82,7 +82,7 @@ public class MainActivity extends Activity {
public void detailContent() { public void detailContent() {
try { try {
Logger.t("detailContent").d(spider.detailContent(Arrays.asList("434686"))); Logger.t("detailContent").d(spider.detailContent(Arrays.asList("78702")));
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -0,0 +1,220 @@
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.bean.uvod.Data;
import com.github.catvod.crawler.Spider;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.AES;
import com.github.catvod.utils.Util;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Qile
* @date 2024/11/15
*/
public class Uvod extends Spider {
private static String siteUrl = "https://api-h5.uvod.tv";
private static final String latest = siteUrl + "/video/latest";
private static final String list = siteUrl + "/video/list";
private static final String detail = siteUrl + "/video/info";
private static final String play = siteUrl + "/video/source";
private static final String publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeBQWotWOpsuPn3PAA+bcmM8YD\n" +
"fEOzPz7hb/vItV43vBJV2FcM72Hdcv3DccIFuEV9LQ8vcmuetld98eksja9vQ1Ol\n" +
"8rTnjpTpMbd4HedevSuIhWidJdMAOJKDE3AgGFcQvQePs80uXY2JhTLkRn2ICmDR\n" +
"/fb32OwWY3QGOvLcuQIDAQAB\n" +
"-----END PUBLIC KEY-----";
private static final String privateKeyPem =
"-----BEGIN PRIVATE KEY-----\n" +
"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJ4FBai1Y6my4+fc\n" +
"8AD5tyYzxgN8Q7M/PuFv+8i1Xje8ElXYVwzvYd1y/cNxwgW4RX0tDy9ya562V33x\n" +
"6SyNr29DU6XytOeOlOkxt3gd5169K4iFaJ0l0wA4koMTcCAYVxC9B4+zzS5djYmF\n" +
"MuRGfYgKYNH99vfY7BZjdAY68ty5AgMBAAECgYB1rbvHJj5wVF7Rf4Hk2BMDCi9+\n" +
"zP4F8SW88Y6KrDbcPt1QvOonIea56jb9ZCxf4hkt3W6foRBwg86oZo2FtoZcpCJ+\n" +
"rFqUM2/wyV4CuzlL0+rNNSq7bga7d7UVld4hQYOCffSMifyF5rCFNH1py/4Dvswm\n" +
"pi5qljf+dPLSlxXl2QJBAMzPJ/QPAwcf5K5nngQtbZCD3nqDFpRixXH4aUAIZcDz\n" +
"S1RNsHrT61mEwZ/thQC2BUJTQNpGOfgh5Ecd1MnURwsCQQDFhAFfmvK7svkygoKX\n" +
"t55ARNZy9nmme0StMOfdb4Q2UdJjfw8+zQNtKFOM7VhB7ijHcfFuGsE7UeXBe20n\n" +
"g/XLAkEAv9SoT2hgJaQxxUk4MCF8pgddstJlq8Z3uTA7JMa4x+kZfXTm/6TOo6I8\n" +
"2VbXZLsYYe8op0lvsoHMFvBSBljV0QJBAKhxyoYRa98dZB5qZRskciaXTlge0WJk\n" +
"kA4vvh3/o757izRlQMgrKTfng1GVfIZFqKtnBiIDWTXQw2N9cnqXtH8CQAx+CD5t\n" +
"l1iT0cMdjvlMg2two3SnpOjpo7gALgumIDHAmsUWhocLtcrnJI032VQSUkNnLq9z\n" +
"EIfmHDz0TPVNHBQ=\n" +
"-----END PRIVATE KEY-----";
private Map<String, String> getHeader(String url) {
String[] item = url.split("\\|");
String URL = item[0];
String tid = item.length > 1 ? item[1] : "";
String pg = item.length > 2 ? item[2] : "";
String hm = String.valueOf(System.currentTimeMillis());
String text = "";
if (URL.equals(latest)) {
text = String.format("-parent_category_id=101-%s", hm);
} else if (URL.equals(list)) {
if (pg != null && !pg.isEmpty()) {
text = String.format("-page=%s&pagesize=42&parent_category_id=%s&sort_type=asc-%s", pg, tid, hm);
} else {
text = String.format("-keyword=%s&need_fragment=1&page=1&pagesize=42&sort_type=asc-%s", URLEncoder.encode(tid).toLowerCase(), hm);
}
} else if (URL.equals(detail)) {
text = String.format("-id=%s-%s", tid, hm);
} else if (URL.equals(play)) {
text = String.format("-quality=4&video_fragment_id=%s&video_id=%s-%s", pg, tid, hm);
}
String sign = AES.MD5(text);
Map<String, String> header = new HashMap<>();
header.put("User-Agent", Util.CHROME);
header.put("referer", "https://www.uvod.tv/");
header.put("origin", "https://www.uvod.tv");
header.put("content-type", "application/json");
header.put("accept", "*/*");
header.put("x-signature", sign);
header.put("x-timestamp", hm);
header.put("x-token", "");
System.out.println(header);
return header;
}
private Map<String, String> playHeader() {
Map<String, String> header = new HashMap<>();
header.put("User-Agent", Util.CHROME);
header.put("referer", "https://www.uvod.tv/");
header.put("origin", "https://www.uvod.tv");
return header;
}
@Override
public void init(Context context, String extend) throws Exception {
if (!extend.isEmpty()) {
siteUrl = extend;
}
}
public String encrypt(String data) throws Exception {
String aesKey = AES.randomKey(32);
String aesEncryptedData = AES.aesEncrypt(data, aesKey, "abcdefghijklmnop");
String rsaEncryptedKey = AES.rsaEncrypt(aesKey, publicKeyPem);
return aesEncryptedData + "." + rsaEncryptedKey;
}
public String decrypt(String encryptedData) throws Exception {
encryptedData = encryptedData.replaceAll("\\s", "");
String[] parts = encryptedData.split("\\.");
if (parts.length != 2) {
return null;
}
String rsaEncryptedKey = parts[1];
String decryptedKey = AES.decryptRSA(rsaEncryptedKey, privateKeyPem);
String aesEncryptedData = parts[0];
return AES.CBC(aesEncryptedData, decryptedKey, "abcdefghijklmnop");
}
@Override
public String homeContent(boolean filter) throws Exception {
List<Class> classes = new ArrayList<>();
List<String> typeIds = Arrays.asList("101", "100", "106", "102", "103", "104", "105");
List<String> typeNames = Arrays.asList("电视剧", "电影", "粤台专区", "综艺", "动漫", "体育", "纪录片");
for (int i = 0; i < typeIds.size(); i++)
classes.add(new Class(typeIds.get(i), typeNames.get(i)));
String param = "{\"parent_category_id\":101}";
String encryptData = encrypt(param);
String content = OkHttp.post(latest, encryptData, getHeader(latest)).getBody();
String decryptData = decrypt(content);
List<Vod> list = new ArrayList<>();
Data data = Data.objectFrom(decryptData);
for (Data.Videolatest video : data.getVideolatest()) {
list.add(video.vod());
}
return Result.string(classes, list);
}
@Override
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
String param = String.format("{\"parent_category_id\":\"%s\",\"category_id\":null,\"language\":null,\"year\":null,\"region\":null,\"state\":null,\"keyword\":\"\",\"paid\":null,\"page\":%s,\"pagesize\":42,\"sort_field\":\"\",\"sort_type\":\"asc\"}", tid, pg);
String encryptData = encrypt(param);
String content = OkHttp.post(list, encryptData, getHeader(list + "|" + tid + "|" + pg)).getBody();
String decryptData = decrypt(content);
List<Vod> list = new ArrayList<>();
Data data = Data.objectFrom(decryptData);
for (Data.Videolatest video : data.getVideolatest()) {
list.add(video.vod());
}
return Result.string(list);
}
@Override
public String detailContent(List<String> ids) throws Exception {
String param = String.format("{\"id\":\"%s\"}", ids.get(0));
String encryptData = encrypt(param);
String content = OkHttp.post(detail, encryptData, getHeader(detail + "|" + ids.get(0))).getBody();
String decryptData = decrypt(content);
Data data = Data.objectFrom(decryptData);
StringBuilder vod_play_url = new StringBuilder();
List<Data.VideoFragmentList> videoFragmentList = data.getVideoFragmentList();
for (int j = 0; j < videoFragmentList.size(); j++) {
Data.VideoFragmentList videolist = videoFragmentList.get(j);
String name = videolist.getSymbol();
String nid = videolist.getId();
nid = ids.get(0) + "|" + nid;
vod_play_url.append(name).append("$").append(nid);
boolean notLastEpisode = j < videoFragmentList.size() - 1;
vod_play_url.append(notLastEpisode ? "#" : "$$$");
}
Data.Video video = data.getVideo();
Vod vod = new Vod();
vod.setVodId(ids.get(0));
vod.setVodYear(video.getYear());
vod.setVodArea(video.getRegion());
vod.setVodActor(video.getStarring());
vod.setVodRemarks(video.getState());
vod.setVodContent(video.getDescription());
vod.setVodDirector(video.getDirector());
vod.setTypeName(video.getLanguage());
vod.setVodPlayFrom("Qile");
vod.setVodPlayUrl(vod_play_url.toString());
return Result.string(vod);
}
@Override
public String searchContent(String key, boolean quick) throws Exception {
String param = String.format("{\"parent_category_id\":null,\"category_id\":null,\"language\":null,\"year\":null,\"region\":null,\"state\":null,\"keyword\":\"%s\",\"paid\":null,\"page\":1,\"pagesize\":42,\"sort_field\":\"\",\"sort_type\":\"asc\",\"need_fragment\":1}", key);
String encryptData = encrypt(param);
String content = OkHttp.post(list, encryptData, getHeader(list + "|" + key)).getBody();
String decryptData = decrypt(content);
List<Vod> list = new ArrayList<>();
Data data = Data.objectFrom(decryptData);
for (Data.Videolatest video : data.getVideolatest()) {
list.add(video.vod());
}
return Result.string(list);
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
String[] item = id.split("\\|");
String tid = item[0];
String nid = item[1];
String param = String.format("{\"video_id\":\"%s\",\"video_fragment_id\":%s,\"quality\":4,\"seek\":null}", tid, nid);
String encryptData = encrypt(param);
String content = OkHttp.post(play, encryptData, getHeader(play + "|" + tid + "|" + nid)).getBody();
String decryptData = decrypt(content);
Data data = Data.objectFrom(decryptData);
Data.Video video = data.getVideo();
String realUrl = video.getUrl();
return Result.get().url(realUrl).header(playHeader()).string();
}
}

View File

@ -0,0 +1,115 @@
package com.github.catvod.utils;
import android.annotation.SuppressLint;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
@SuppressLint("NewApi")
public static String CBC(String src, String KEY, String IV) {
try {
src = src.replace("\\", "");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES");
AlgorithmParameterSpec paramSpec = new IvParameterSpec(IV.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(src));
return new String(decrypted);
} catch (Exception ignored) {
}
return null;
}
@SuppressLint("NewApi")
public static String aesEncrypt(String data, String key, String iv) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().withoutPadding().encodeToString(encrypted);
}
@SuppressLint("NewApi")
public static String rsaEncrypt(String data, String publicKeyPem) throws Exception {
String publicKeyPEM = publicKeyPem.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s+", "");
byte[] decoded = Base64.getDecoder().decode(publicKeyPEM);
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(spec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
}
@SuppressLint("NewApi")
public static String decryptRSA(String encryptedKey, String privateKeyPem) throws Exception {
String privateKeyPEM = privateKeyPem.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s", "");
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyPEM);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedKey));
return new String(decrypted, StandardCharsets.UTF_8);
}
public static String MD5(String src) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(src.getBytes(Charset.forName("UTF-8")));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
int v = bytes[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
sb.append(0);
}
sb.append(hv);
}
return sb.toString().toLowerCase();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
public static String randomKey(int size) {
String keys = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
StringBuilder key = new StringBuilder();
for (int i = 0; i < size; i++) {
int pos = (int) Math.floor(Math.random() * keys.length());
key.append(keys.charAt(pos));
}
return key.toString();
}
}

Binary file not shown.

View File

@ -1 +1 @@
343fe4d32b861b765cadbab3e28b618a 150642bf400eaca2938193dc2d092663