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 e8494501..0e59ac8b 100644 --- a/app/src/main/java/com/github/catvod/bean/Vod.java +++ b/app/src/main/java/com/github/catvod/bean/Vod.java @@ -3,6 +3,9 @@ package com.github.catvod.bean; import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; +import java.util.ArrayList; +import java.util.List; + public class Vod { @SerializedName("type_name") @@ -171,4 +174,46 @@ public class Vod { this.ratio = ratio; } } + public static class VodPlayBuilder{ + private List vodPlayFrom = new ArrayList(); + private List vodPlayUrl = new ArrayList(); + + /** + * 格式: from name1$$$name2$$$name3 + * url name$url#name2$url2$$$(分类2)$$分类3 + * @param playFrom + * @param playUrl + */ + public VodPlayBuilder append(String playFrom, List playUrl){ + vodPlayFrom.add(playFrom); + vodPlayUrl.add(toPlayUrlStr(playUrl)); + return this; + } + + public BuildResult build(){ + BuildResult buildResult = new BuildResult(); + buildResult.vodPlayFrom = String.join("$$$", vodPlayFrom); + buildResult.vodPlayUrl = String.join("$$$", vodPlayUrl); + return buildResult; + } + + private String toPlayUrlStr(List playUrl) { + List list = new ArrayList<>(); + for (PlayUrl url : playUrl) { + list.add(url.name.replace("m3u8", "") + '$' + url.url); + } + return String.join("#", list); + } + + public static class BuildResult{ + public String vodPlayFrom; + public String vodPlayUrl; + } + + public static class PlayUrl { + public String flag; // 线路标志 + public String name; + public String url; + } + } } diff --git a/app/src/main/java/com/github/catvod/spider/Glod.kt b/app/src/main/java/com/github/catvod/spider/Glod.kt new file mode 100644 index 00000000..d99eb605 --- /dev/null +++ b/app/src/main/java/com/github/catvod/spider/Glod.kt @@ -0,0 +1,184 @@ +package com.github.catvod.spider + + +import com.github.catvod.bean.Class +import com.github.catvod.bean.Result +import com.github.catvod.bean.Vod + +import com.github.catvod.crawler.Spider +import com.github.catvod.crawler.SpiderDebug +import com.github.catvod.net.OkHttp +import com.github.catvod.utils.Json +import com.github.catvod.utils.Util +import com.google.gson.JsonArray +import org.apache.commons.lang3.time.DateFormatUtils +import org.apache.commons.lang3.time.DateUtils +import org.jsoup.Jsoup +import java.util.* + +class Glod : Spider() { + private val host = Util.base64Decode("aHR0cHM6Ly93d3cuY2Zrajg2LmNvbS8=") + + private val epUrl = "/api/mw-movie/anonymous/v1/video/episode/url?id=%s&nid=%s" + + private val deviceId = UUID.randomUUID().toString(); + + private val classList = listOf(Class("1", "电影"), Class("2", "电视剧"), Class("4", "动漫"), Class("3", "综艺")) + + override fun homeContent(filter: Boolean): String { + val string = OkHttp.string(host, Util.webHeaders("https://www.bing.com")) + val vodList = parseFromJson(string, "home") +// val vodList = parseVodList(string) + return Result.string(classList, vodList) + } + + override fun detailContent(ids: MutableList): String { + val url = host + ids[0] + val string = OkHttp.string(url, Util.webHeaders(host)) + val parse = Jsoup.parse(string) + val name = parse.select("h1.title").text() + val img = parse.select("div[class^=detail__CardImg] img").attr("src") + val tag = parse.select("div.tags > a.tag").eachText().joinToString(" ") + val vod = Vod(ids[0], name, img, tag) + val director = parse.select("div.director") + val d = director[0].select("a").text() + val actor = director[1].select("a").eachText().joinToString(" ") + vod.setVodActor(actor) + vod.setVodDirector(d) + val desc = parse.select("div.intro div.wrapper_more_text").text() + vod.vodContent = desc + + val linkList = parse.select("div.listitem > a") + + val playUrlList = mutableListOf() + for (element in linkList) { + val u = element.attr("href") + val n = element.text() + playUrlList.add(Vod.VodPlayBuilder.PlayUrl().also { + it.name = n + it.url = u + }) + } + + val buildResult = Vod.VodPlayBuilder().append("glod", playUrlList).build() + + val time = parse.select("div.item:contains(上映时间)").select(".item-top").text() + vod.setVodYear(DateFormatUtils.format(DateUtils.parseDate(time, "yyyy-MM-dd"), "yyyy")) + vod.setVodPlayFrom(buildResult.vodPlayFrom) + vod.vodPlayUrl = buildResult.vodPlayUrl + return Result.string(vod) + } + + /** + * 请求头 + * t 时间戳 + * sign 签名 + * deviceId + * authorization 空的 + * 还有cookie + */ + override fun playerContent(flag: String, id: String, vipFlags: MutableList): String { + val list = id.split("/") + val i = list[3] + val nid = list[5] + val webHeaders = Util.webHeaders(host) + val time = Date().time.toString() + val sign = Util.sha1Hex( + Util.MD5("id=${i}&nid=${nid}&key=cb808529bae6b6be45ecfab29a4889bc&t=${time}") + ) + webHeaders["t"] = time + webHeaders["deviceId"] = deviceId + webHeaders["Sign"] = sign + + val string = OkHttp.string(host + String.format(epUrl, i, nid), webHeaders) + val parse = Json.parse(string).asJsonObject + if (parse.get("code").asInt != 200) { + SpiderDebug.log("glod 获取播放链接失败:$string") + return Result.error("获取播放链接失败") + } + val url = parse.get("data").asJsonObject.get("playUrl").asString + return Result.get().url(url).string() + } + + override fun categoryContent(tid: String, pg: String, filter: Boolean, extend: HashMap): String { + val url = "$host/type/$tid" + val string = OkHttp.string(url, Util.webHeaders(host)) + val vodList = parseFromJson(string, "cate") + return Result.string(classList, vodList) + } + + override fun searchContent(key: String, quick: Boolean): String { + val string = OkHttp.string("${host}vod/search/$key", Util.webHeaders(host)) + val vodList = parseFromJson(string, "search") + return Result.string(vodList) + } + + private fun parseFromJson(string: String, type: String): List { + val vodList = mutableListOf() + val parse = Jsoup.parse(string) + val select = parse.select("script") + val data = select.find { + it.html().contains("操作成功") + } + if (data == null) { + SpiderDebug.log("glod 找不到json") + return vodList + } + val json = data.html().replace("self.__next_f.push(", "").replace(")", "") + + val gson = Json.parse(json).asJsonArray.get(1).asString.replace("6:", "") + val resp = Json.parse(gson).asJsonArray.get(3).asJsonObject + if (type == "home") { + val element = resp.get("children").asJsonArray.get(3).asJsonObject.get("data").asJsonObject.get("data") + var vList = element.asJsonObject.get("homeNewMoviePageData").asJsonObject.get("list").asJsonArray + getVodList(vList, vodList) + vList = element.asJsonObject.get("homeBroadcastPageData").asJsonObject.get("list").asJsonArray + getVodList(vList, vodList) + vList = element.asJsonObject.get("homeManagerPageData").asJsonObject.get("list").asJsonArray + getVodList(vList, vodList) + vList = element.asJsonObject.get("newestTvPageData").asJsonObject.get("list").asJsonArray + getVodList(vList, vodList) + vList = element.asJsonObject.get("newestCartoonPageData").asJsonObject.get("list").asJsonArray + getVodList(vList, vodList) + } else if (type == "cate") { + for (jsonElement in resp.get("children").asJsonArray.get(3).asJsonObject.get("data").asJsonArray) { + val objList = jsonElement.asJsonObject.get("vodList").asJsonObject.get("list").asJsonArray + getVodList(objList, vodList) + } + } else if (type == "search") { + val asJsonArray = + resp.get("data").asJsonObject.get("data").asJsonObject.get("result").asJsonObject.get("list").asJsonArray + getVodList(asJsonArray, vodList) + } + return vodList + } + + private fun getVodList( + objList: JsonArray, vodList: MutableList + ) { + for (oj in objList) { + val obj = oj.asJsonObject + val v = Vod() + v.setVodId("/detail/" + obj.get("vodId").asString) + v.setVodName(obj.get("vodName").asString) + // v.setVodActor(obj.get("vodActor").asString) + v.setVodRemarks(obj.get("vodScore").asString) + v.setVodPic(obj.get("vodPic").asString) + vodList.add(v) + } + } + + private fun parseVodList(string: String): MutableList { + val parse = Jsoup.parse(string) + val list = parse.select("div.content-card") + val vodList = mutableListOf() + for (element in list) { + val id = element.select("a").attr("href") + val title = element.select("div.info-title-box > div.title").text() + val score = element.select("div.bottom div[class^=score]").text() + val img = element.select("img").attr("srcset") + vodList.add(Vod(id, title, host + img, score)) + } + return vodList + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/catvod/utils/Util.java b/app/src/main/java/com/github/catvod/utils/Util.java index 28a65ae9..8f628690 100644 --- a/app/src/main/java/com/github/catvod/utils/Util.java +++ b/app/src/main/java/com/github/catvod/utils/Util.java @@ -14,12 +14,15 @@ import android.webkit.WebViewClient; import com.github.catvod.spider.Init; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.mozilla.universalchardet.UniversalDetector; import java.math.BigInteger; import java.net.URI; import java.nio.charset.Charset; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -338,6 +341,31 @@ public class Util { } + + + public static String sha1Hex(String input) throws NoSuchAlgorithmException { + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] messageDigest = md.digest(input.getBytes(Charset.defaultCharset())); + return bytesToHex(messageDigest); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + public static String bytesToHex(byte[] bytes) { + StringBuilder hexString = new StringBuilder(); + for (byte b : bytes) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + public static class LCSResult { public int length; public String sequence; diff --git a/app/src/test/java/GoldTest.java b/app/src/test/java/GoldTest.java new file mode 100644 index 00000000..eb51f0c2 --- /dev/null +++ b/app/src/test/java/GoldTest.java @@ -0,0 +1,95 @@ +import android.app.Application; +import com.github.catvod.spider.ChangZhang; +import com.github.catvod.spider.Glod; +import com.github.catvod.spider.Init; +import com.github.catvod.utils.Json; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import org.junit.Assert; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +@RunWith(RobolectricTestRunner.class) +public class GoldTest { + // @Mock + private Application mockContext; + + private Glod spider; + + @org.junit.Before + public void setUp() throws Exception { + mockContext = RuntimeEnvironment.application; + Init.init(mockContext); + spider = new Glod(); + spider.init(mockContext, "https://www.czzy.site/"); + } + + @org.junit.Test + public void homeContent() throws Exception { + String content = spider.homeContent(true); + JsonObject map = Json.safeObject(content); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + System.out.println("homeContent--" + gson.toJson(map)); + + //Assert.assertFalse(map.getAsJsonArray("list").isEmpty()); + } + + @org.junit.Test + public void homeVideoContent() throws Exception { + String content = spider.homeVideoContent(); + JsonObject map = Json.safeObject(content); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + System.out.println("homeVideoContent--" + gson.toJson(map)); + + //Assert.assertFalse(map.getAsJsonArray("list").isEmpty()); + } + + @org.junit.Test + public void categoryContent() throws Exception { + String content = spider.categoryContent("2", "2", true, new HashMap<>()); + JsonObject map = Json.safeObject(content); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + System.out.println("categoryContent--" + gson.toJson(map)); + Assert.assertFalse(map.getAsJsonArray("list").isEmpty()); + } + + @org.junit.Test + public void detailContent() throws Exception { + + String content = spider.detailContent(Arrays.asList("/detail/126164")); + JsonObject map = Json.safeObject(content); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + System.out.println("detailContent--" + gson.toJson(map)); + Assert.assertFalse(map.getAsJsonArray("list").isEmpty()); + } + + @org.junit.Test + public void playerContent() throws Exception { + String froms = "glod"; + String urls = "1$/vod/play/126164/sid/1057548#2$/vod/play/126164/sid/1057549#3$/vod/play/126164/sid/1057735#4$/vod/play/126164/sid/1057683"; + for (int i = 0; i < urls.split("\\$\\$\\$").length; i++) { + String content = spider.playerContent(froms.split("\\$\\$\\$")[i], urls.split("\\$\\$\\$")[i].split("#")[0].split("\\$")[1], new ArrayList<>()); + JsonObject map = Json.safeObject(content); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + System.out.println("playerContent--" + gson.toJson(map)); + Assert.assertFalse(map.getAsJsonPrimitive("url").getAsString().isEmpty()); + } + } + + @org.junit.Test + public void searchContent() throws Exception { + String content = spider.searchContent("红", false); + JsonObject map = Json.safeObject(content); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + System.out.println("searchContent--" + gson.toJson(map)); + Assert.assertFalse(map.getAsJsonArray("list").isEmpty()); + } +} \ No newline at end of file diff --git a/jar/custom_spider.jar b/jar/custom_spider.jar index ed2fb758..b910b517 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 1585c6f7..59f18f18 100644 --- a/jar/custom_spider.jar.md5 +++ b/jar/custom_spider.jar.md5 @@ -1 +1 @@ -0dcb7bdf2bfb2e77fdea1d66f53d1d60 +1766329347d5e433c0983b6800348229 diff --git a/json/index.json b/json/index.json index 672a9461..fc93e702 100644 --- a/json/index.json +++ b/json/index.json @@ -1,5 +1,5 @@ { - "spider": "https://androidcatvodspider.pages.dev/jar/custom_spider.jar;md5;0dcb7bdf2bfb2e77fdea1d66f53d1d60", + "spider": "https://androidcatvodspider.pages.dev/jar/custom_spider.jar;md5;1766329347d5e433c0983b6800348229", "lives": [ { "name": "直播ipv6", @@ -190,6 +190,15 @@ "changeable": 0, "ext": {} }, + { + "key": "glod", + "name": "金牌 | 影视", + "type": 3, + "api": "csp_Glod", + "searchable": 1, + "changeable": 0, + "ext": {} + }, { "key": "newvision", "name":"(js)新视觉影院(不稳定)", diff --git a/json/index1.json b/json/index1.json index 7c998d8f..45531c46 100644 --- a/json/index1.json +++ b/json/index1.json @@ -1,5 +1,5 @@ { - "spider": "https://androidcatvodspider.pages.dev/jar/custom_spider.jar;md5;0dcb7bdf2bfb2e77fdea1d66f53d1d60", + "spider": "https://androidcatvodspider.pages.dev/jar/custom_spider.jar;md5;1766329347d5e433c0983b6800348229", "lives": [ { "name": "直播ipv6", diff --git a/json/index2.json b/json/index2.json index 161e6652..198a969c 100644 --- a/json/index2.json +++ b/json/index2.json @@ -1,5 +1,5 @@ { - "spider": "https://androidcatvodspider.pages.dev/jar/custom_spider.jar;md5;0dcb7bdf2bfb2e77fdea1d66f53d1d60", + "spider": "https://androidcatvodspider.pages.dev/jar/custom_spider.jar;md5;1766329347d5e433c0983b6800348229", "lives": [ { "name": "直播ipv6", @@ -190,6 +190,15 @@ "changeable": 0, "ext": {} }, + { + "key": "glod", + "name": "金牌 | 影视", + "type": 3, + "api": "csp_Glod", + "searchable": 1, + "changeable": 0, + "ext": {} + }, { "key": "newvision", "name":"(js)新视觉影院(不稳定)",