forked from recloudstream/cloudstream
more links gogo, fixed #503
This commit is contained in:
parent
26ba1de2f2
commit
45567fb4f4
4 changed files with 160 additions and 34 deletions
|
@ -267,10 +267,24 @@ abstract class MainAPI {
|
||||||
/** Might need a different implementation for desktop*/
|
/** Might need a different implementation for desktop*/
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
fun base64Decode(string: String): String {
|
fun base64Decode(string: String): String {
|
||||||
|
return String(base64DecodeArray(string), Charsets.ISO_8859_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
fun base64DecodeArray(string: String): ByteArray {
|
||||||
return try {
|
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) {
|
} 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,3 +32,23 @@ fun <T, R> Iterable<T>.pmap(
|
||||||
fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking {
|
fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking {
|
||||||
map { async { f(it) } }.map { it.await() }
|
map { async { f(it) } }.map { it.await() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run code in parallel
|
||||||
|
fun <R> 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 <R> argamap(
|
||||||
|
// vararg transforms: () -> R,
|
||||||
|
//) = runBlocking {
|
||||||
|
// transforms.map { async { it.invoke() } }.map { it.await() }
|
||||||
|
//}
|
|
@ -1,11 +1,15 @@
|
||||||
package com.lagradost.cloudstream3.animeproviders
|
package com.lagradost.cloudstream3.animeproviders
|
||||||
|
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.spec.IvParameterSpec
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
class GogoanimeProvider : MainAPI() {
|
class GogoanimeProvider : MainAPI() {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -24,6 +28,26 @@ class GogoanimeProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val qualityRegex = Regex("(\\d+)P")
|
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"
|
override val mainUrl = "https://gogoanime.wiki"
|
||||||
|
@ -193,11 +217,28 @@ class GogoanimeProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class GogoSources(
|
||||||
|
val source: List<GogoSource>?,
|
||||||
|
val sourceBk: List<GogoSource>?,
|
||||||
|
//val track: List<Any?>,
|
||||||
|
//val advertising: List<Any?>,
|
||||||
|
//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) {
|
private fun extractVideos(uri: String, callback: (ExtractorLink) -> Unit) {
|
||||||
val doc = app.get(uri).document
|
val doc = app.get(uri).document
|
||||||
|
|
||||||
val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe").attr("src")) ?: return
|
val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe").attr("src")) ?: return
|
||||||
|
|
||||||
|
argpmap(
|
||||||
|
{
|
||||||
val link = iframe.replace("streaming.php", "download")
|
val link = iframe.replace("streaming.php", "download")
|
||||||
val page = app.get(link, headers = mapOf("Referer" to iframe))
|
val page = app.get(link, headers = mapOf("Referer" to iframe))
|
||||||
|
|
||||||
|
@ -205,7 +246,8 @@ class GogoanimeProvider : MainAPI() {
|
||||||
if (it.hasAttr("download")) {
|
if (it.hasAttr("download")) {
|
||||||
val qual = if (it.text()
|
val qual = if (it.text()
|
||||||
.contains("HDP")
|
.contains("HDP")
|
||||||
) "1080" else qualityRegex.find(it.text())?.destructured?.component1().toString()
|
) "1080" else qualityRegex.find(it.text())?.destructured?.component1()
|
||||||
|
.toString()
|
||||||
callback(
|
callback(
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
"Gogoanime",
|
"Gogoanime",
|
||||||
|
@ -221,15 +263,65 @@ class GogoanimeProvider : MainAPI() {
|
||||||
loadExtractor(url, null, callback)
|
loadExtractor(url, null, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe))
|
val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe))
|
||||||
streamingResponse.document.select(".list-server-items > .linkserver")
|
val streamingDocument = streamingResponse.document
|
||||||
|
argpmap({
|
||||||
|
streamingDocument.select(".list-server-items > .linkserver")
|
||||||
?.forEach { element ->
|
?.forEach { element ->
|
||||||
val status = element.attr("data-status") ?: return@forEach
|
val status = element.attr("data-status") ?: return@forEach
|
||||||
if (status != "1") return@forEach
|
if (status != "1") return@forEach
|
||||||
val data = element.attr("data-video") ?: return@forEach
|
val data = element.attr("data-video") ?: return@forEach
|
||||||
loadExtractor(data, streamingResponse.url, callback)
|
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 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<GogoSources>(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(
|
override suspend fun loadLinks(
|
||||||
|
|
|
@ -52,7 +52,7 @@ enum class Qualities(var value: Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getQualityFromName(qualityName: String): Int {
|
fun getQualityFromName(qualityName: String): Int {
|
||||||
return when (qualityName.replace("p", "").replace("P", "")) {
|
return when (qualityName.replace("p", "").replace("P", "").trim()) {
|
||||||
"360" -> Qualities.P360
|
"360" -> Qualities.P360
|
||||||
"480" -> Qualities.P480
|
"480" -> Qualities.P480
|
||||||
"720" -> Qualities.P720
|
"720" -> Qualities.P720
|
||||||
|
|
Loading…
Reference in a new issue