diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index ede6ed25..0c685488 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -267,10 +267,24 @@ abstract class MainAPI { /** Might need a different implementation for desktop*/ @SuppressLint("NewApi") fun base64Decode(string: String): String { + return String(base64DecodeArray(string), Charsets.ISO_8859_1) +} + +@SuppressLint("NewApi") +fun base64DecodeArray(string: String): ByteArray { return try { - String(android.util.Base64.decode(string, android.util.Base64.DEFAULT), Charsets.ISO_8859_1) + android.util.Base64.decode(string, android.util.Base64.DEFAULT) } catch (e: Exception) { - String(Base64.getDecoder().decode(string)) + Base64.getDecoder().decode(string) + } +} + +@SuppressLint("NewApi") +fun base64Encode(array: ByteArray): String { + return try { + String(android.util.Base64.encode(array, android.util.Base64.NO_WRAP), Charsets.ISO_8859_1) + } catch (e: Exception) { + String(Base64.getEncoder().encode(array)) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt b/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt index ea592c15..f3be00f6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt @@ -29,6 +29,26 @@ fun Iterable.pmap( return ArrayList(destination) } -fun List.apmap(f: suspend (A) -> B): List = runBlocking { +fun List.apmap(f: suspend (A) -> B): List = runBlocking { map { async { f(it) } }.map { it.await() } -} \ No newline at end of file +} + +// run code in parallel +fun argpmap( + vararg transforms: () -> R, + numThreads: Int = maxOf(Runtime.getRuntime().availableProcessors() - 2, 1), + exec: ExecutorService = Executors.newFixedThreadPool(numThreads) +) { + for (item in transforms) { + exec.submit { item.invoke() } + } + + exec.shutdown() + exec.awaitTermination(1, TimeUnit.DAYS) +} + +//fun argamap( +// vararg transforms: () -> R, +//) = runBlocking { +// transforms.map { async { it.invoke() } }.map { it.await() } +//} \ No newline at end of file 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 29ebe0bd..37582883 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt @@ -1,11 +1,15 @@ package com.lagradost.cloudstream3.animeproviders import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.Jsoup import java.util.* +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec class GogoanimeProvider : MainAPI() { companion object { @@ -24,6 +28,26 @@ class GogoanimeProvider : MainAPI() { } val qualityRegex = Regex("(\\d+)P") + + // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60 + // No Licence on the function + private fun cryptoHandler( + string: String, + iv: ByteArray, + secretKeyString: ByteArray, + encrypt: Boolean = true + ): String { + val ivParameterSpec = IvParameterSpec(iv) + val secretKey = SecretKeySpec(secretKeyString, "AES") + val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") + return if (!encrypt) { + cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec) + String(cipher.doFinal(base64DecodeArray(string))) + } else { + cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec) + base64Encode(cipher.doFinal(string.toByteArray())) + } + } } override val mainUrl = "https://gogoanime.wiki" @@ -193,43 +217,111 @@ class GogoanimeProvider : MainAPI() { } } + data class GogoSources( + val source: List?, + val sourceBk: List?, + //val track: List, + //val advertising: List, + //val linkiframe: String + ) + + data class GogoSource( + val file: String, + val label: String?, + val type: String?, + val default: String? = null + ) + private fun extractVideos(uri: String, callback: (ExtractorLink) -> Unit) { val doc = app.get(uri).document val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe").attr("src")) ?: return - val link = iframe.replace("streaming.php", "download") - val page = app.get(link, headers = mapOf("Referer" to iframe)) + argpmap( + { + val link = iframe.replace("streaming.php", "download") + val page = app.get(link, headers = mapOf("Referer" to iframe)) - page.document.select(".dowload > a").pmap { - if (it.hasAttr("download")) { - val qual = if (it.text() - .contains("HDP") - ) "1080" else qualityRegex.find(it.text())?.destructured?.component1().toString() - callback( - ExtractorLink( - "Gogoanime", - if (qual == "null") "Gogoanime" else "Gogoanime - " + qual + "p", - it.attr("href"), - page.url, - getQualityFromName(qual), - it.attr("href").contains(".m3u8") - ) - ) - } else { - val url = it.attr("href") - loadExtractor(url, null, callback) - } - } + page.document.select(".dowload > a").pmap { + if (it.hasAttr("download")) { + val qual = if (it.text() + .contains("HDP") + ) "1080" else qualityRegex.find(it.text())?.destructured?.component1() + .toString() + callback( + ExtractorLink( + "Gogoanime", + if (qual == "null") "Gogoanime" else "Gogoanime - " + qual + "p", + it.attr("href"), + page.url, + getQualityFromName(qual), + it.attr("href").contains(".m3u8") + ) + ) + } else { + val url = it.attr("href") + loadExtractor(url, null, callback) + } + } + }, { + val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe)) + val streamingDocument = streamingResponse.document + argpmap({ + streamingDocument.select(".list-server-items > .linkserver") + ?.forEach { element -> + val status = element.attr("data-status") ?: return@forEach + if (status != "1") return@forEach + val data = element.attr("data-video") ?: return@forEach + 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 + val encrypted = + streamingDocument.select("script[data-name='crypto']").attr("data-value") + val iv = streamingDocument.select("script[data-name='ts']").attr("data-value") + .toByteArray() - val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe)) - streamingResponse.document.select(".list-server-items > .linkserver") - ?.forEach { element -> - val status = element.attr("data-status") ?: return@forEach - if (status != "1") return@forEach - val data = element.attr("data-video") ?: return@forEach - loadExtractor(data, streamingResponse.url, callback) + val id = Regex("id=([^&]+)").find(iframe)!!.value.removePrefix("id=") + + val secretKey = cryptoHandler(encrypted, iv, iv + iv, false) + val encryptedId = + cryptoHandler(id, "0000000000000000".toByteArray(), secretKey.toByteArray()) + + val jsonResponse = + app.get( + "http://gogoplay.io/encrypt-ajax.php?id=$encryptedId&time=00000000000000000000", + headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ) + val sources = AppUtils.parseJson(jsonResponse.text) + + fun invokeGogoSource( + source: GogoSource, + sourceCallback: (ExtractorLink) -> Unit + ) { + sourceCallback.invoke( + ExtractorLink( + this.name, + "${this.name} ${source.label?.replace("0 P","0p") ?: ""}", + source.file, + "", + getQualityFromName(source.label ?: ""), + isM3u8 = source.type == "hls" + ) + ) + } + + sources.source?.forEach { + println("${this.name} ${it.label ?: ""}") + invokeGogoSource(it, callback) + } + sources.sourceBk?.forEach { + println("${this.name} ${it.label ?: ""}") + invokeGogoSource(it, callback) + } + }) } + ) } override suspend fun loadLinks( diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index f22d653a..d1eb1b54 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -52,7 +52,7 @@ enum class Qualities(var value: Int) { } fun getQualityFromName(qualityName: String): Int { - return when (qualityName.replace("p", "").replace("P", "")) { + return when (qualityName.replace("p", "").replace("P", "").trim()) { "360" -> Qualities.P360 "480" -> Qualities.P480 "720" -> Qualities.P720