From 5a006377682cb4593dcbf843b3ec19ea293d259a Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Thu, 17 Mar 2022 16:04:05 +0100 Subject: [PATCH] cracked encryption and added more servers on AsianLoad, DramaSee and WatchAsian --- .../animeproviders/GogoanimeProvider.kt | 177 +++++++++++------- .../cloudstream3/extractors/Vidstream.kt | 1 - .../movieproviders/AsianLoadProvider.kt | 3 + .../movieproviders/DramaSeeProvider.kt | 66 ++++--- .../VidstreamProviderTemplate.kt | 10 + .../movieproviders/WatchAsianProvider.kt | 81 ++++---- 6 files changed, 209 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt index d090063a..7f33955e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt @@ -2,9 +2,10 @@ package com.lagradost.cloudstream3.animeproviders import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.utils.* -import okio.ByteString.Companion.decodeHex import org.jsoup.Jsoup +import java.net.URI import java.util.* import javax.crypto.Cipher import javax.crypto.spec.IvParameterSpec @@ -47,13 +48,112 @@ class GogoanimeProvider : MainAPI() { base64Encode(cipher.doFinal(string.toByteArray())) } } - } - private fun String.decodeHex(): ByteArray { - check(length % 2 == 0) { "Must have an even length" } - return chunked(2) - .map { it.toInt(16).toByte() } - .toByteArray() + private fun String.decodeHex(): ByteArray { + check(length % 2 == 0) { "Must have an even length" } + return chunked(2) + .map { it.toInt(16).toByte() } + .toByteArray() + } + + /** + * @param iframeUrl something like https://gogoplay4.com/streaming.php?id=XXXXXX + * @param mainApiName used for ExtractorLink names and source + * @param iv secret iv from site, required non-null + * @param secretKey secret key for decryption from site, required non-null + * */ + suspend fun extractVidstream( + iframeUrl: String, + mainApiName: String, + callback: (ExtractorLink) -> Unit, + iv: ByteArray?, + secretKey: ByteArray? + ) = safeApiCall { + // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt + // No Licence on the following code + // Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt + // License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE + + if (iv == null || secretKey == null) + return@safeApiCall + + val uri = URI(iframeUrl) + val mainUrl = "https://" + uri.host + + val id = Regex("id=([^&]+)").find(iframeUrl)!!.value.removePrefix("id=") + val encryptedId = cryptoHandler(id, iv, secretKey) + val jsonResponse = + app.get( + "$mainUrl/encrypt-ajax.php?id=$encryptedId", + headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ) + val dataencrypted = + jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}") + val datadecrypted = cryptoHandler(dataencrypted, iv, secretKey, false) + val sources = AppUtils.parseJson(datadecrypted) + println("GET SOURCES $sources $iframeUrl") + + fun invokeGogoSource( + source: GogoSource, + sourceCallback: (ExtractorLink) -> Unit + ) { + when { + source.file.contains("m3u8") -> { + M3u8Helper().m3u8Generation( + M3u8Helper.M3u8Stream( + source.file, + headers = mapOf("Referer" to "https://gogoplay4.com") + ), true + ) + .map { stream -> + val qualityString = + if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p" + sourceCallback( + ExtractorLink( + mainApiName, + "$mainApiName $qualityString", + stream.streamUrl, + mainUrl, + getQualityFromName(stream.quality.toString()), + true + ) + ) + } + } + source.file.contains("vidstreaming") -> { + sourceCallback.invoke( + ExtractorLink( + mainApiName, + "$mainApiName ${source.label?.replace("0 P", "0p") ?: ""}", + source.file, + mainUrl, + getQualityFromName(source.label ?: ""), + isM3u8 = source.type == "hls" + ) + ) + } + else -> { + sourceCallback.invoke( + ExtractorLink( + mainApiName, + "$mainApiName ${source.label?.replace("0 P", "0p") ?: ""}", + source.file, + mainUrl, + getQualityFromName(source.label ?: ""), + isM3u8 = source.type == "hls" + ) + ) + } + } + } + + sources.source?.forEach { + invokeGogoSource(it, callback) + } + sources.sourceBk?.forEach { + invokeGogoSource(it, callback) + } + } } override val mainUrl = "https://gogoanime.film" @@ -281,67 +381,10 @@ class GogoanimeProvider : MainAPI() { loadExtractor(data, streamingResponse.url, callback) } }, { - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt - // No Licence on the following code - // Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt - // License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE - val iv = "31323835363732333833393339383532".decodeHex() - val secretKey = "3235373136353338353232393338333936313634363632323738383333323838".decodeHex() - - val id = Regex("id=([^&]+)").find(iframe)!!.value.removePrefix("id=") - val encryptedId = cryptoHandler(id, iv, secretKey) - val jsonResponse = - app.get( - "http://gogoplay4.com/encrypt-ajax.php?id=$encryptedId&time=00000000000000000000", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ) - val dataencrypted = jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}") - val datadecrypted = cryptoHandler(dataencrypted, iv, secretKey, false) - val sources = AppUtils.parseJson(datadecrypted) - - fun invokeGogoSource( - source: GogoSource, - sourceCallback: (ExtractorLink) -> Unit - ) { - if (source.file.contains("m3u8")) { - M3u8Helper().m3u8Generation( - M3u8Helper.M3u8Stream( - source.file, - headers = mapOf("Referer" to "https://gogoplay4.com") - ), true - ) - .map { stream -> - val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p" - sourceCallback( ExtractorLink( - name, - "$name $qualityString", - stream.streamUrl, - "https://gogoplay4.com", - getQualityFromName(stream.quality.toString()), - true - )) - } - } else if (source.file.contains("vidstreaming")) { - sourceCallback.invoke( - ExtractorLink( - this.name, - "${this.name} ${source.label?.replace("0 P", "0p") ?: ""}", - source.file, - "https://gogoplay4.com", - getQualityFromName(source.label ?: ""), - isM3u8 = source.type == "hls" - ) - ) - } - } - - sources.source?.forEach { - invokeGogoSource(it, callback) - } - sources.sourceBk?.forEach { - invokeGogoSource(it, callback) - } + val secretKey = + "3235373136353338353232393338333936313634363632323738383333323838".decodeHex() + extractVidstream(iframe, this.name, callback, iv, secretKey) }) } ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidstream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidstream.kt index e961a966..47c6fac0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidstream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidstream.kt @@ -32,7 +32,6 @@ class Vidstream(val mainUrl: String) { isCasting: Boolean = false, callback: (ExtractorLink) -> Unit ): Boolean { - println("VIDSTREAM:: $id") val extractorUrl = getExtractorUrl(id) argamap( { diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AsianLoadProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AsianLoadProvider.kt index f190324a..f82ad8cd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AsianLoadProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AsianLoadProvider.kt @@ -17,5 +17,8 @@ class AsianLoadProvider : VidstreamProviderTemplate() { "$mainUrl/ongoing-series" ) + override val iv = "9262859232435825".toByteArray() + override val secretKey = "93422192433952489752342908585752".toByteArray() + override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DramaSeeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DramaSeeProvider.kt index 671fdbbe..cdef6ae0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DramaSeeProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DramaSeeProvider.kt @@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.movieproviders import com.fasterxml.jackson.module.kotlin.readValue import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream import com.lagradost.cloudstream3.extractors.* import com.lagradost.cloudstream3.extractors.helper.AsianEmbedHelper import com.lagradost.cloudstream3.utils.AppUtils.toJson @@ -22,31 +23,32 @@ class DramaSeeProvider : MainAPI() { val document = app.get(mainUrl, headers = headers).document val mainbody = document.getElementsByTag("body") - return HomePageResponse(mainbody?.select("section")?.map { row -> - val main = row?.select("main") ?: return@map null - val title = main.select("div.title > div > h2")?.text() ?: "Main" - val inner = main.select("li.series-item") ?: return@map null + return HomePageResponse( + mainbody?.select("section")?.map { row -> + val main = row?.select("main") ?: return@map null + val title = main.select("div.title > div > h2")?.text() ?: "Main" + val inner = main.select("li.series-item") ?: return@map null - HomePageList( - title, - inner.mapNotNull { - // Get inner div from article - val innerBody = it?.selectFirst("a") - // Fetch details - val link = fixUrlNull(innerBody?.attr("href")) ?: return@mapNotNull null - val image = fixUrlNull(innerBody?.select("img")?.attr("src")) ?: "" - val name = it?.selectFirst("a.series-name")?.text() ?: "" - //Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}") - MovieSearchResponse( - name, - link, - this.name, - TvType.TvSeries, - image, - year = null, - id = null, - ) - }.distinctBy { c -> c.url }) + HomePageList( + title, + inner.mapNotNull { + // Get inner div from article + val innerBody = it?.selectFirst("a") + // Fetch details + val link = fixUrlNull(innerBody?.attr("href")) ?: return@mapNotNull null + val image = fixUrlNull(innerBody?.select("img")?.attr("src")) ?: "" + val name = it?.selectFirst("a.series-name")?.text() ?: "" + //Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}") + MovieSearchResponse( + name, + link, + this.name, + TvType.TvSeries, + image, + year = null, + id = null, + ) + }.distinctBy { c -> c.url }) }?.filterNotNull() ?: listOf() ) } @@ -55,7 +57,7 @@ class DramaSeeProvider : MainAPI() { val url = "$mainUrl/search?q=$query" val html = app.get(url).document val document = html.getElementsByTag("body") - .select("section > main > ul.series > li") ?: return listOf() + .select("section > main > ul.series > li") ?: return listOf() return document.mapNotNull { if (it == null) { @@ -88,12 +90,17 @@ class DramaSeeProvider : MainAPI() { val poster = fixUrlNull(inner?.select("div.img > img")?.attr("src")) ?: "" //Log.i(this.name, "Result => (imgLinkCode) ${imgLinkCode}") val title = inner?.select("h1.series-name")?.text() ?: "" - val year = if (title.length > 5) { title.substring(title.length - 5) - .trim().trimEnd(')').toIntOrNull() } else { null } + val year = if (title.length > 5) { + title.substring(title.length - 5) + .trim().trimEnd(')').toIntOrNull() + } else { + null + } //Log.i(this.name, "Result => (year) ${title.substring(title.length - 5)}") val seriesBody = body?.select("div.series-body") val descript = seriesBody?.firstOrNull()?.select("div.js-content")?.text() - val tags = seriesBody?.select("div.series-tags > a")?.mapNotNull { it?.text()?.trim() ?: return@mapNotNull null } + val tags = seriesBody?.select("div.series-tags > a") + ?.mapNotNull { it?.text()?.trim() ?: return@mapNotNull null } val recs = body?.select("ul.series > li")?.mapNotNull { val a = it.select("a.series-img") ?: return@mapNotNull null val aUrl = fixUrlNull(a.attr("href")) ?: return@mapNotNull null @@ -190,6 +197,9 @@ class DramaSeeProvider : MainAPI() { //Log.i(this.name, "Result => (url) ${url}") when { url.startsWith("https://asianembed.io") || url.startsWith("https://asianload.io") -> { + val iv = "9262859232435825".toByteArray() + val secretKey = "93422192433952489752342908585752".toByteArray() + extractVidstream(url, this.name, callback, iv, secretKey) AsianEmbedHelper.getUrls(url, callback) } url.startsWith("https://embedsito.com") -> { diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VidstreamProviderTemplate.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VidstreamProviderTemplate.kt index d2851673..e5731e25 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VidstreamProviderTemplate.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VidstreamProviderTemplate.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.movieproviders import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream import com.lagradost.cloudstream3.extractors.Vidstream import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName @@ -14,6 +15,14 @@ open class VidstreamProviderTemplate : MainAPI() { open val homePageUrlList = listOf() open val vidstreamExtractorUrl: String? = null + /** + * Used to generate encrypted video links. + * Try keys from other providers before cracking + * one yourself. + * */ + open val iv: ByteArray? = null + open val secretKey: ByteArray? = null + // // mainUrl is good to have as a holder for the url to make future changes easier. // override val mainUrl: String // get() = "https://vidembed.cc" @@ -212,6 +221,7 @@ open class VidstreamProviderTemplate : MainAPI() { val iframeLink = Jsoup.parse(app.get(data).text).selectFirst("iframe")?.attr("src") ?: return false + extractVidstream(iframeLink, this.name, callback, iv, secretKey) // In this case the video player is a vidstream clone and can be handled by the vidstream extractor. // This case is a both unorthodox and you normally do not call extractors as they detect the url returned and does the rest. val vidstreamObject = Vidstream(vidstreamExtractorUrl ?: mainUrl) diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/WatchAsianProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/WatchAsianProvider.kt index d1b04251..140bb214 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/WatchAsianProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/WatchAsianProvider.kt @@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.movieproviders import com.fasterxml.jackson.module.kotlin.readValue import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream import com.lagradost.cloudstream3.extractors.XStreamCdn import com.lagradost.cloudstream3.extractors.helper.AsianEmbedHelper import com.lagradost.cloudstream3.utils.AppUtils.toJson @@ -35,32 +36,35 @@ class WatchAsianProvider : MainAPI() { return HomePageResponse( rowPair.mapNotNull { row -> - val main = (doc.select("div.tab-content.${row.second}") - ?: doc.select("div.tab-content.${row.second}.selected")) ?: return@mapNotNull null + val main = (doc.select("div.tab-content.${row.second}") + ?: doc.select("div.tab-content.${row.second}.selected")) + ?: return@mapNotNull null - val title = row.first - val inner = main.select("li") ?: return@mapNotNull null + val title = row.first + val inner = main.select("li") ?: return@mapNotNull null - HomePageList( - title, - inner.map { - // Get inner div from article - val innerBody = it?.selectFirst("a") - // Fetch details - val link = fixUrlNull(innerBody?.attr("href")) ?: return@map null - val image = fixUrlNull(innerBody?.select("img")?.attr("data-original")) ?: "" - val name = (innerBody?.selectFirst("h3.title")?.text() ?: innerBody?.text())?: "" - //Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}") - MovieSearchResponse( - name, - link, - this.name, - TvType.TvSeries, - image, - year = null, - id = null, - ) - }.filterNotNull().distinctBy { c -> c.url }) + HomePageList( + title, + inner.map { + // Get inner div from article + val innerBody = it?.selectFirst("a") + // Fetch details + val link = fixUrlNull(innerBody?.attr("href")) ?: return@map null + val image = + fixUrlNull(innerBody?.select("img")?.attr("data-original")) ?: "" + val name = (innerBody?.selectFirst("h3.title")?.text() ?: innerBody?.text()) + ?: "" + //Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}") + MovieSearchResponse( + name, + link, + this.name, + TvType.TvSeries, + image, + year = null, + id = null, + ) + }.filterNotNull().distinctBy { c -> c.url }) }.filter { a -> a.list.isNotEmpty() } ) } @@ -68,13 +72,15 @@ class WatchAsianProvider : MainAPI() { override suspend fun search(query: String): List { val url = "$mainUrl/search?type=movies&keyword=$query" val document = app.get(url).document.getElementsByTag("body") - .select("div.block.tab-container > div > ul > li") ?: return listOf() + .select("div.block.tab-container > div > ul > li") ?: return listOf() return document.mapNotNull { val innerA = it?.selectFirst("a") ?: return@mapNotNull null val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null val title = it.select("h3.title")?.text() ?: return@mapNotNull null - if (title.isEmpty()) { return@mapNotNull null } + if (title.isEmpty()) { + return@mapNotNull null + } val year = null val imgsrc = innerA.select("img")?.attr("data-original") ?: return@mapNotNull null val image = fixUrlNull(imgsrc) @@ -96,9 +102,9 @@ class WatchAsianProvider : MainAPI() { val isDramaDetail = url.contains("/drama-detail/") var poster = "" var title = "" - var descript : String? = null - var year : Int? = null - var tags : List? = null + var descript: String? = null + var year: Int? = null + var tags: List? = null if (isDramaDetail) { val main = body.select("div.details") val inner = main?.select("div.info") @@ -110,7 +116,9 @@ class WatchAsianProvider : MainAPI() { descript = inner?.text() inner?.select("p")?.forEach { p -> - val caption = p?.selectFirst("span")?.text()?.trim()?.lowercase()?.removeSuffix(":")?.trim() ?: return@forEach + val caption = + p?.selectFirst("span")?.text()?.trim()?.lowercase()?.removeSuffix(":")?.trim() + ?: return@forEach when (caption) { "genre" -> { tags = p.select("a")?.mapNotNull { it?.text()?.trim() } @@ -132,7 +140,9 @@ class WatchAsianProvider : MainAPI() { year = if (title.length > 5) { title.replace(")", "").replace("(", "").substring(title.length - 5) .trim().trimEnd(')').toIntOrNull() - } else { null } + } else { + null + } } // Episodes Links @@ -194,7 +204,9 @@ class WatchAsianProvider : MainAPI() { ): Boolean { val links = if (data.startsWith(mainUrl)) { getServerLinks(data) - } else { data } + } else { + data + } var count = 0 mapper.readValue>(links).apmap { item -> count++ @@ -202,6 +214,9 @@ class WatchAsianProvider : MainAPI() { //Log.i(this.name, "Result => (url) $url") when { url.startsWith("https://asianembed.io") || url.startsWith("https://asianload.io") -> { + val iv = "9262859232435825".toByteArray() + val secretKey = "93422192433952489752342908585752".toByteArray() + extractVidstream(url, this.name, callback, iv, secretKey) AsianEmbedHelper.getUrls(url, callback) } url.startsWith("https://embedsito.com") -> { @@ -219,7 +234,7 @@ class WatchAsianProvider : MainAPI() { return count > 0 } - private suspend fun getServerLinks(url: String) : String { + private suspend fun getServerLinks(url: String): String { val moviedoc = app.get(url, referer = mainUrl).document return moviedoc.select("div.anime_muti_link > ul > li") ?.mapNotNull {