From 33797895e29ea38801d64fdd4d05f96a7e3fd4d8 Mon Sep 17 00:00:00 2001 From: lushunming <1357197829@qq.com> Date: Wed, 14 Aug 2024 10:33:52 +0800 Subject: [PATCH] kotlin and ngkt --- app/build.gradle | 5 + .../java/com/github/catvod/spider/NGkt.kt | 277 ++++++++++++++++++ app/src/test/java/NGTest.java | 5 +- build.gradle | 1 + jar/custom_spider.jar | Bin 359089 -> 420319 bytes jar/custom_spider.jar.md5 | 2 +- json/index.json | 4 +- json/index1.json | 2 +- 8 files changed, 290 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/com/github/catvod/spider/NGkt.kt diff --git a/app/build.gradle b/app/build.gradle index 725488af..d462c6db 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ plugins { id 'com.android.application' id 'ru.cleverpumpkin.proguard-dictionaries-generator' + id 'org.jetbrains.kotlin.android' } java { toolchain { @@ -39,6 +40,9 @@ android { unitTests.returnDefaultValues = true unitTests.includeAndroidResources = true } + kotlinOptions { + jvmTarget = '1.8' + } configurations.configureEach { resolutionStrategy { @@ -57,6 +61,7 @@ dependencies { implementation 'com.google.zxing:core:3.3.0' implementation 'com.orhanobut:logger:2.2.0' implementation 'org.jsoup:jsoup:1.15.3' + implementation 'androidx.core:core-ktx:1.10.1' // Required -- JUnit 4 framework testImplementation 'junit:junit:4.12' diff --git a/app/src/main/java/com/github/catvod/spider/NGkt.kt b/app/src/main/java/com/github/catvod/spider/NGkt.kt new file mode 100644 index 00000000..3f005bdb --- /dev/null +++ b/app/src/main/java/com/github/catvod/spider/NGkt.kt @@ -0,0 +1,277 @@ +package com.github.catvod.spider + + +import com.github.catvod.bean.Class +import com.github.catvod.bean.Filter +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.ProxyVideo +import com.github.catvod.utils.Util +import com.google.gson.JsonParser +import com.google.gson.annotations.SerializedName +import com.google.gson.reflect.TypeToken +import java.net.URLEncoder + + +class NGkt : Spider() { + private var COMMON_URL: String = Util.base64Decode("aHR0cDovL3lzLmNoYW5nbWVuZ3l1bi5jb20=") + private val FIND_VIDEO_VOD_LIST: String = "/api.php/provide/vod_list" + private val FIND_CLASSIFICATION: String = "/api.php/provide/home_nav" + private val VIDEO_DETAIL: String = "/api.php/provide/vod_detail" + private val SEARCH_SEARCH: String = "/api.php/provide/search_result" + + + private val classList = mutableListOf() + private val filters = LinkedHashMap>() + + + private fun getParams(): Map { + val hashMap: HashMap = java.util.HashMap() + hashMap["devices"] = "android" + hashMap["deviceModel"] = "ASUS_I003DD" + hashMap["deviceBrand"] = "ASUS" + hashMap["deviceVersion"] = "9" + hashMap["deviceScreen"] = "2340*1080" + hashMap["appVersionCode"] = "9" + hashMap["appVersionName"] = "1.0.9" + hashMap["time"] = (System.currentTimeMillis() / 1000).toString() + hashMap["imei"] = "" + hashMap["app"] = "ylys" + return hashMap + } + + fun getHeaders(): Map { + val headers = HashMap() + val currentTimeMillis = System.currentTimeMillis() + headers.put("timeMillis", currentTimeMillis.toString()) + headers.put( + "sign", Util.MD5(Util.base64Decode("I3VCRnN6ZEVNMG9MMEpSbkA=") + "$currentTimeMillis") + ) + return headers + } + + override fun homeContent(filter: Boolean): String { + if (classList.isEmpty()) { + val string = OkHttp.string("$COMMON_URL$FIND_CLASSIFICATION", getParams(), getHeaders()) + val filterList = JsonParser.parseString(string).asJsonArray + for ((index, jsonElement) in filterList.withIndex()) { + if (index == 0) continue + val obj = jsonElement.asJsonObject + val id = obj.get("id").asInt + val name = obj.get("name").asString + val clazz = Class(id.toString(), name) + classList.add(clazz) +// val msgs = obj.get("msg").asJsonArray +// if(msgs.isEmpty){ +// continue +// } +// for (msg in msgs) { +// val msgObj = msg.asJsonObject +// val dataArray = msgObj.get("data").asJsonArray +// val list = mutableListOf() +// for ((index, jsonElement1) in dataArray.withIndex()) { +// if(index == 0) continue +// list.add(Filter.Value(jsonElement1.asString, jsonElement1.asString)) +// } +// +// if(filters[id.toString()].isNullOrEmpty()){ +// filters[id.toString()] = mutableListOf(Filter(msgObj.get("name").asString, dataArray.get(0).asString, list)) +// }else{ +// filters[id.toString()]!!.add(Filter(msgObj.get("name").asString, dataArray.get(0).asString, list)) +// } +// } + } + } + return Result.string(classList, filters) + } + + override fun categoryContent( + tid: String, pg: String, filter: Boolean, extend: java.util.HashMap + ): String { + val params = getParams().toMutableMap() + extend.forEach { (t, u) -> + params[t] = URLEncoder.encode(u, "UTF-8") + } + params["page"] = pg + params["id"] = tid + val string = OkHttp.string("$COMMON_URL$FIND_VIDEO_VOD_LIST", params, getHeaders()) + val type = object : TypeToken>() {}.type + val resp = Json.parseSafe>(string, type) + var vodList = listOf() + if (resp.isSuccess()) { + vodList = resp.list.toVodList() + } else { + SpiderDebug.log("ng cate error: $string") + } + return Result.string(classList, vodList, filters) + } + + override fun detailContent(ids: MutableList): String { + val map = getParams().toMutableMap() + map["id"] = ids[0] + val string = OkHttp.string("$COMMON_URL$VIDEO_DETAIL", map, getHeaders()) + val type = object : TypeToken>() {}.type + val dt = Json.parseSafe>(string, type) + if (!dt.isSuccess()) { + SpiderDebug.log("ng detail err: ${dt.msg}") + return Result.error(dt.msg) + } + return Result.string(dt.data.toVod().apply { setVodId(ids[0]) }) + } + + override fun playerContent(flag: String, id: String, vipFlags: MutableList): String { + val string = OkHttp.string(id) + val type = object : TypeToken>() {}.type + val rst = Json.parseSafe>(string, type) + if (!rst.isSuccess()) { + SpiderDebug.log("play err: ${rst.msg}") + return Result.error(rst.msg) + } + val filter = rst.data.header.filter { it.key.equals("User-Agent", true) } + return Result.get().url(ProxyVideo.buildCommonProxyUrl(rst.data.url, filter)).string() + } + + override fun searchContent(key: String, quick: Boolean): String { + val params = getParams().toMutableMap() + params["video_name"] = URLEncoder.encode(key, "UTF-8") + val string = OkHttp.string("$COMMON_URL$SEARCH_SEARCH", params, getHeaders()) + val type = object : TypeToken>>() {}.type + val rst = Json.parseSafe>>(string, type) + if (!rst.isSuccess()) { + SpiderDebug.log("ng search error:${rst.msg}") + return Result.error(rst.msg) + } + return Result.string(rst.data[0].toVodList()) + } + + + data class Rst( + val code: Int, + val msg: String, + val limit: String, + val pagecount: Int, + val total: Int, + val list: List, + val data: T + ) { + fun isSuccess(): Boolean { + return code == 1 + } + } + + + data class It( + val id: Int, + val img: String, + val name: String, + val score: String, + val msg: String, + ) { + fun toVod(): Vod { + val vod = Vod() + vod.setVodId(id.toString()) + vod.setVodName(name) + vod.setVodRemarks(score) + vod.setVodPic(img) + return vod + } + } + + data class Dt( + val name: String, + val year: String, + val score: String, + val hits: Int, + val type: String, + val img: String, + val info: String, + @SerializedName("total_count") val totalCount: Int, + @SerializedName("player_info") val playerInfo: List + ) { + fun toVod(): Vod { + val vod = Vod() + vod.setVodId(name + score) + vod.setVodName(name) + vod.setVodPic(img) + vod.setVodTag(type) + vod.setVodRemarks(year) + vod.vodContent = info + val playFrom = StringBuilder() + val playUrl = StringBuilder() + for ((_, _, show, _, videoInfo) in playerInfo) { + playFrom.append(show).append("$$$") + for ((_, name1, _, url) in videoInfo) { + playUrl.append(name1).append("$").append(url[0]) + playUrl.append("#") + } + playUrl.append("$$$") + } + vod.setVodPlayFrom(playFrom.toString()) + vod.vodPlayUrl = playUrl.toString() + return vod + } + } + + data class DtIt( + val id: Int, + val from: String, + val show: String, + @SerializedName("url_count") val urlCount: Int, + @SerializedName("video_info") val videoInfo: List + ) + + data class VtInfo( + val id: Int, val name: String, val pic: String, val url: List + ) + + private fun List.toVodList(): MutableList { + val list = mutableListOf() + for (it in this) { + list.add(it.toVod()) + } + return list + } + + data class PlayRst( + val url: String, val header: Map + ) + + data class SearchRst( + val id: Int, val name: String, val data: List + ) { + fun toVodList(): MutableList { + val list = mutableListOf() + for (datum in data) { + list.add(datum.toVOd()) + } + return list + } + } + + data class SearchRstItem( + val id: Int, + val type: Int, + @SerializedName("video_name") val videoName: String, + val qingxidu: String, + val img: String, + val director: String, + @SerializedName("main_actor") val mainActor: String, + val category: String + ) { + fun toVOd(): Vod { + val vod = Vod() + vod.setVodId(id.toString()) + vod.setVodTag(qingxidu) + vod.setVodPic(img) + vod.setVodRemarks(category) + vod.setVodName(videoName) + vod.setVodActor(mainActor) + return vod + } + } + +} \ No newline at end of file diff --git a/app/src/test/java/NGTest.java b/app/src/test/java/NGTest.java index fa959541..7d0e1aae 100644 --- a/app/src/test/java/NGTest.java +++ b/app/src/test/java/NGTest.java @@ -2,6 +2,7 @@ import android.app.Application; import com.github.catvod.spider.Init; import com.github.catvod.spider.NG; +import com.github.catvod.spider.NGkt; import com.github.catvod.utils.Json; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -21,13 +22,13 @@ public class NGTest { // @Mock private Application mockContext; - private NG spider; + private NGkt spider; @org.junit.Before public void setUp() throws Exception { mockContext = RuntimeEnvironment.application; Init.init(mockContext); - spider = new NG(); + spider = new NGkt(); spider.init(mockContext, "https://www.czzy.site/"); } diff --git a/build.gradle b/build.gradle index 4f8a5bab..bcf49e38 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ plugins { id 'ru.cleverpumpkin.proguard-dictionaries-generator' version '1.0.8' apply false id 'com.android.application' version '8.5.0' apply false id 'com.android.library' version '8.5.0' apply false + id 'org.jetbrains.kotlin.android' version '2.0.10' apply false } tasks.register('clean', Delete) { diff --git a/jar/custom_spider.jar b/jar/custom_spider.jar index b0c7579588614e045a7b3b6c7bb44045e8431a32..24c750b2e3a95f0ea267ead13ae3d9b78e664777 100644 GIT binary patch literal 420319 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