From 8a62fb592802a87cbe964ba24d6c70b9180d1898 Mon Sep 17 00:00:00 2001 From: MUEDSA <7676275+muedsa@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:40:16 +0800 Subject: [PATCH] update: more play source decrypt --- .github/workflows/{android.yml => build.yml} | 2 +- .github/workflows/source_validate.yml | 25 +++++++ .../com/muedsa/jcytv/util/JcyHtmlTool.kt | 68 ++++++++++++------- .../com/muedsa/jcytv/util/JcyHtmlToolTest.kt | 28 ++------ .../jcytv/util/JcyPlayUrlDecryptValidator.kt | 21 +++++- 5 files changed, 98 insertions(+), 46 deletions(-) rename .github/workflows/{android.yml => build.yml} (97%) create mode 100644 .github/workflows/source_validate.yml diff --git a/.github/workflows/android.yml b/.github/workflows/build.yml similarity index 97% rename from .github/workflows/android.yml rename to .github/workflows/build.yml index 473062f..124679e 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Android CI +name: Android Build on: push: diff --git a/.github/workflows/source_validate.yml b/.github/workflows/source_validate.yml new file mode 100644 index 0000000..949627c --- /dev/null +++ b/.github/workflows/source_validate.yml @@ -0,0 +1,25 @@ +name: Play Source Valid + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '23 3 * * *' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew :app:testDebugUnitTest --tests "com.muedsa.jcytv.util.JcyPlayUrlDecryptValidator" \ No newline at end of file diff --git a/app/src/main/kotlin/com/muedsa/jcytv/util/JcyHtmlTool.kt b/app/src/main/kotlin/com/muedsa/jcytv/util/JcyHtmlTool.kt index 123ca4f..a39af03 100644 --- a/app/src/main/kotlin/com/muedsa/jcytv/util/JcyHtmlTool.kt +++ b/app/src/main/kotlin/com/muedsa/jcytv/util/JcyHtmlTool.kt @@ -1,6 +1,5 @@ package com.muedsa.jcytv.util -import com.badlogic.gdx.maps.MapObject import com.google.common.net.HttpHeaders import com.muedsa.jcytv.model.JcyRankVideoInfo import com.muedsa.jcytv.model.JcyRawPlaySource @@ -8,7 +7,6 @@ import com.muedsa.jcytv.model.JcySimpleVideoInfo import com.muedsa.jcytv.model.JcyVideoDetail import com.muedsa.uitl.decodeBase64 import com.muedsa.uitl.decryptAES128CBCPKCS7 -import com.muedsa.uitl.encodeBase64 import com.muedsa.uitl.encryptRC4 import org.jsoup.Jsoup import org.jsoup.nodes.Document @@ -29,15 +27,19 @@ object JcyHtmlTool { const val DETAIL_URL = "https://9ciyuan.com/index.php/vod/detail/id/{id}.html" - val DECRYPT_DEFAULT : (String) -> String = { key: String -> key } + val DECRYPT_DEFAULT: (String) -> String = { key: String -> key } + + val DECRYPT_NOT_SUPPORT: (String) -> String = + { _: String -> throw IllegalStateException("不支持的播放源") } private val SILISILI_ENCRYPTED_URL_REGEX = Regex("\"url\":\"([A-Za-z0-9+/=\\\\]*?)\"") private val SILISILI_UID_REGEX = Regex("\"uid\":\"([A-Za-z0-9+/=\\\\]*?)\"") val DECRYPT_SILISILI: (String) -> String = { key: String -> - val doc: Document = Jsoup.connect("https://play.silisili.top/player/ec.php?code=ttnb&if=1&url=$key") - .header(HttpHeaders.REFERER, MAIN_SITE_URL) - .get() + val doc: Document = + Jsoup.connect("https://play.silisili.top/player/ec.php?code=ttnb&if=1&url=$key") + .header(HttpHeaders.REFERER, MAIN_SITE_URL) + .get() val bodyHtml = doc.body().html() val urlMatchResult = SILISILI_ENCRYPTED_URL_REGEX.find(bodyHtml) val uidMatchResult = SILISILI_UID_REGEX.find(bodyHtml) @@ -56,27 +58,37 @@ object JcyHtmlTool { val bodyHtml = doc.body().html() val urlMatchResult = DILIDILI_ENCRYPTED_URL_REGEX.find(bodyHtml) val encryptedUrl = urlMatchResult!!.groupValues[1].replace("\\/", "/") - URLDecoder.decode(encryptedUrl.decodeBase64() - .encryptRC4("202205051426239465".toByteArray()) - .decodeToString(), - StandardCharsets.UTF_8.name()) + URLDecoder.decode( + encryptedUrl.decodeBase64() + .encryptRC4("202205051426239465".toByteArray()) + .decodeToString(), + StandardCharsets.UTF_8.name() + ) + } + + val DECRYPT_LIBILIBI: (String) -> String = { key: String -> + if (key.endsWith(".m3u8", true) + || key.endsWith(".mp4", true) + || key.endsWith(".flv", true) + ) key + else TODO("NOT_SUPPORT") } - val PLAYER_SITE_MAP: Map String> = mapOf( + val PLAYER_SITE_MAP: Map String> = mapOf( "NBY" to DECRYPT_DILIDILI, // ✅囧次元N https://v.dilidili.ink/player/?url= "ttnb" to DECRYPT_DILIDILI, // https://v.dilidili.ink/player/?url= "lzm3u8" to DECRYPT_DILIDILI, // ✅囧次元Z https://v.dilidili.ink/player/?url= "snm3u8" to DECRYPT_DILIDILI, // ✅囧次元O https://v.dilidili.ink/player/?url= "cycp" to DECRYPT_DEFAULT, // ? - "ffm3u8" to DECRYPT_DEFAULT, // ✅囧次元A https://play.libilibi.top/?url= + "ffm3u8" to DECRYPT_LIBILIBI, // ✅囧次元A https://play.libilibi.top/?url= "SLNB" to DECRYPT_DEFAULT, // ? - "dplayer" to DECRYPT_DEFAULT, // dplayer - "videojs" to DECRYPT_DEFAULT, // videojs + "dplayer" to DECRYPT_NOT_SUPPORT, // 不支持dplayer + "videojs" to DECRYPT_NOT_SUPPORT, // 不支持videojs "iva" to DECRYPT_DEFAULT, - "iframe" to DECRYPT_DEFAULT, // 不支持iframe - "link" to DECRYPT_DEFAULT, // 不支持link - "swf" to DECRYPT_DEFAULT, // 不支持swf - "flv" to DECRYPT_DEFAULT, // 不支持flv + "iframe" to DECRYPT_NOT_SUPPORT, // 不支持iframe + "link" to DECRYPT_NOT_SUPPORT, // 不支持link + "swf" to DECRYPT_NOT_SUPPORT, // 不支持swf + "flv" to DECRYPT_DEFAULT, "ACG" to DECRYPT_SILISILI, // https://play.silisili.top/player/ec.php?code=ttnb&if=1&url= "languang" to DECRYPT_DEFAULT, // TODO APP线路 https://player.123tv.icu/player/ec.php?code=yunq&if=1&url= ) @@ -115,7 +127,9 @@ object JcyHtmlTool { title = a.text(), subTitle = nameDiv.selectFirst("p")!!.text(), detailPagePath = a.attr("href"), - imageUrl = getAbsoluteUrl(imgDiv.selectFirst(".img-wrapper")!!.attr("data-original")) + imageUrl = getAbsoluteUrl( + imgDiv.selectFirst(".img-wrapper")!!.attr("data-original") + ) ) } } @@ -129,7 +143,9 @@ object JcyHtmlTool { title = nameDiv.selectFirst("h4")!!.text(), subTitle = nameDiv.selectFirst("p")!!.text(), detailPagePath = it.selectFirst("a")!!.attr("href"), - imageUrl = getAbsoluteUrl(imgDiv.selectFirst(".img-wrapper")!!.attr("data-original")) + imageUrl = getAbsoluteUrl( + imgDiv.selectFirst(".img-wrapper")!!.attr("data-original") + ) ) } else { val imgDiv = it.selectFirst(".pic")!! @@ -139,7 +155,9 @@ object JcyHtmlTool { title = a.text(), subTitle = nameDiv.selectFirst("p")!!.text(), detailPagePath = a.attr("href"), - imageUrl = getAbsoluteUrl(imgDiv.selectFirst(".img-wrapper")!!.attr("data-original")) + imageUrl = getAbsoluteUrl( + imgDiv.selectFirst(".img-wrapper")!!.attr("data-original") + ) ) } } @@ -166,7 +184,9 @@ object JcyHtmlTool { title = infoDiv.selectFirst("h4")!!.text(), subTitle = infoDiv.selectFirst("p")!!.text(), detailPagePath = li.selectFirst("a")!!.attr("href"), - imageUrl = getAbsoluteUrl(li.selectFirst(".img-wrapper")!!.attr("img-wrapper")), + imageUrl = getAbsoluteUrl( + li.selectFirst(".img-wrapper")!!.attr("img-wrapper") + ), hotNum = li.selectFirst(".ranking-item-hits")!!.text().toInt(), index = li.selectFirst(".ranking-item-num")!!.text().toInt() ) @@ -212,7 +232,9 @@ object JcyHtmlTool { description = body.selectFirst(".vod-info .info .text")!!.text() .replace("简介:", "") .trim(), - imageUrl = getAbsoluteUrl(body.selectFirst(".vod-info .pic img")!!.attr("data-original")), + imageUrl = getAbsoluteUrl( + body.selectFirst(".vod-info .pic img")!!.attr("data-original") + ), playList = playList ) } diff --git a/app/src/test/kotlin/com/muedsa/jcytv/util/JcyHtmlToolTest.kt b/app/src/test/kotlin/com/muedsa/jcytv/util/JcyHtmlToolTest.kt index 8aa2d8b..b7b4a8c 100644 --- a/app/src/test/kotlin/com/muedsa/jcytv/util/JcyHtmlToolTest.kt +++ b/app/src/test/kotlin/com/muedsa/jcytv/util/JcyHtmlToolTest.kt @@ -14,22 +14,6 @@ class JcyHtmlToolTest { Security.addProvider(BouncyCastleProvider()) } -// @Test -// fun decryptPlayUrl_test() { -// val url = JcyHtmlTool.decryptPlayUrl( -// "pUP07m71iJsJEVHJNNmUWc7KxjyjGHFxbcUUx5HUjUn+/vTBo7+VWEZQE9gyDN13akUf1lpx1EpdDb6bkl3xrYG7/ZMlKzav3cqgWd8cXy5RS5lGh3OOhlU2aKQjliqwsILEoZ8CfXHAJ8XC43/E/MKgwtiKSjSCoiURCldrDvN8w+9L1NOzJODWqpTNL66t/L2/KAKzHg1wmvMr7HC+f/nntQ8qqnd0WsTMsnOaE5ksEY7Jo36ZJkixaccsq+PXs0ECK54TNNu2a734aLm7bz0TeAHCUdtSNSR8BOeenq7TS4xiaeGuU1C3eLK+vfaY4WmUX8QcGYd41V55msAPH9XIYP6PtknZYau9I2H/c0IlyRMMx1W6WTW3r5nMi3oURarJG964uJgMzrxDpLTBJw==", -// "VMnMnV" -// ) -// println(url) -// } -// -// @Test -// fun getDecryptPlayUrlForUrl_test() { -// val url = -// JcyHtmlTool.getDecryptPlayUrlForUrl("https://play.silisili.top/player/ec.php?code=ttnb&if=1&url=acg-oN1bTZQhisyrB20UkQ5sdtfszxbEw9UECamaNW45S1Q=") -// println(url) -// } - @Test fun getVideoDetailById_test() { val id = 3010L @@ -132,11 +116,13 @@ class JcyHtmlToolTest { @Test fun catalog_test() { - val list = JcyHtmlTool.catalog(mapOf( - "id" to "20", - "page" to "2", - "area" to "日本", - )) + val list = JcyHtmlTool.catalog( + mapOf( + "id" to "20", + "page" to "2", + "area" to "日本", + ) + ) list.forEach { println("${it.title} ${it.subTitle} ${it.detailPagePath} ${it.imageUrl}") } diff --git a/app/src/test/kotlin/com/muedsa/jcytv/util/JcyPlayUrlDecryptValidator.kt b/app/src/test/kotlin/com/muedsa/jcytv/util/JcyPlayUrlDecryptValidator.kt index a672ae8..3e58482 100644 --- a/app/src/test/kotlin/com/muedsa/jcytv/util/JcyPlayUrlDecryptValidator.kt +++ b/app/src/test/kotlin/com/muedsa/jcytv/util/JcyPlayUrlDecryptValidator.kt @@ -1,5 +1,24 @@ package com.muedsa.jcytv.util -// todo Validator +import org.junit.Test +import java.net.URL + class JcyPlayUrlDecryptValidator { + + @Test + fun dilidili_js_keyword_valid() { + val text = getHttpContent("https://v.dilidili.ink/mizhiplayerapi/js/setting.js") + check(text.indexOf("YKQ.play(rc4(config.url,'202205051426239465',1));") >= -1) + } + + @Test + fun libilibi_js_keyword_valid() { + val text = getHttpContent("https://play.libilibi.top/js/setting.js") + check(text.indexOf("\$.post(\"\", {") >= -1) + } + + private fun getHttpContent(url: String): String = URL(url).openConnection() + .getInputStream().use { + it.readAllBytes().decodeToString() + } } \ No newline at end of file