cracked encryption and added more servers on AsianLoad, DramaSee and WatchAsian

This commit is contained in:
Blatzar 2022-03-17 16:04:05 +01:00
parent fbfcde0295
commit 5a00637768
6 changed files with 209 additions and 129 deletions

View file

@ -2,9 +2,10 @@ package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import okio.ByteString.Companion.decodeHex
import org.jsoup.Jsoup import org.jsoup.Jsoup
import java.net.URI
import java.util.* import java.util.*
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.IvParameterSpec
@ -47,13 +48,112 @@ class GogoanimeProvider : MainAPI() {
base64Encode(cipher.doFinal(string.toByteArray())) base64Encode(cipher.doFinal(string.toByteArray()))
} }
} }
}
private fun String.decodeHex(): ByteArray { private fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" } check(length % 2 == 0) { "Must have an even length" }
return chunked(2) return chunked(2)
.map { it.toInt(16).toByte() } .map { it.toInt(16).toByte() }
.toByteArray() .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<GogoSources>(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" override val mainUrl = "https://gogoanime.film"
@ -281,67 +381,10 @@ class GogoanimeProvider : MainAPI() {
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
// 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 iv = "31323835363732333833393339383532".decodeHex()
val secretKey = "3235373136353338353232393338333936313634363632323738383333323838".decodeHex() val secretKey =
"3235373136353338353232393338333936313634363632323738383333323838".decodeHex()
val id = Regex("id=([^&]+)").find(iframe)!!.value.removePrefix("id=") extractVidstream(iframe, this.name, callback, iv, secretKey)
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<GogoSources>(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)
}
}) })
} }
) )

View file

@ -32,7 +32,6 @@ class Vidstream(val mainUrl: String) {
isCasting: Boolean = false, isCasting: Boolean = false,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
println("VIDSTREAM:: $id")
val extractorUrl = getExtractorUrl(id) val extractorUrl = getExtractorUrl(id)
argamap( argamap(
{ {

View file

@ -17,5 +17,8 @@ class AsianLoadProvider : VidstreamProviderTemplate() {
"$mainUrl/ongoing-series" "$mainUrl/ongoing-series"
) )
override val iv = "9262859232435825".toByteArray()
override val secretKey = "93422192433952489752342908585752".toByteArray()
override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie)
} }

View file

@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream
import com.lagradost.cloudstream3.extractors.* import com.lagradost.cloudstream3.extractors.*
import com.lagradost.cloudstream3.extractors.helper.AsianEmbedHelper import com.lagradost.cloudstream3.extractors.helper.AsianEmbedHelper
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
@ -22,31 +23,32 @@ class DramaSeeProvider : MainAPI() {
val document = app.get(mainUrl, headers = headers).document val document = app.get(mainUrl, headers = headers).document
val mainbody = document.getElementsByTag("body") val mainbody = document.getElementsByTag("body")
return HomePageResponse(mainbody?.select("section")?.map { row -> return HomePageResponse(
val main = row?.select("main") ?: return@map null mainbody?.select("section")?.map { row ->
val title = main.select("div.title > div > h2")?.text() ?: "Main" val main = row?.select("main") ?: return@map null
val inner = main.select("li.series-item") ?: return@map null val title = main.select("div.title > div > h2")?.text() ?: "Main"
val inner = main.select("li.series-item") ?: return@map null
HomePageList( HomePageList(
title, title,
inner.mapNotNull { inner.mapNotNull {
// Get inner div from article // Get inner div from article
val innerBody = it?.selectFirst("a") val innerBody = it?.selectFirst("a")
// Fetch details // Fetch details
val link = fixUrlNull(innerBody?.attr("href")) ?: return@mapNotNull null val link = fixUrlNull(innerBody?.attr("href")) ?: return@mapNotNull null
val image = fixUrlNull(innerBody?.select("img")?.attr("src")) ?: "" val image = fixUrlNull(innerBody?.select("img")?.attr("src")) ?: ""
val name = it?.selectFirst("a.series-name")?.text() ?: "<Untitled>" val name = it?.selectFirst("a.series-name")?.text() ?: "<Untitled>"
//Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}") //Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}")
MovieSearchResponse( MovieSearchResponse(
name, name,
link, link,
this.name, this.name,
TvType.TvSeries, TvType.TvSeries,
image, image,
year = null, year = null,
id = null, id = null,
) )
}.distinctBy { c -> c.url }) }.distinctBy { c -> c.url })
}?.filterNotNull() ?: listOf() }?.filterNotNull() ?: listOf()
) )
} }
@ -55,7 +57,7 @@ class DramaSeeProvider : MainAPI() {
val url = "$mainUrl/search?q=$query" val url = "$mainUrl/search?q=$query"
val html = app.get(url).document val html = app.get(url).document
val document = html.getElementsByTag("body") val document = html.getElementsByTag("body")
.select("section > main > ul.series > li") ?: return listOf() .select("section > main > ul.series > li") ?: return listOf()
return document.mapNotNull { return document.mapNotNull {
if (it == null) { if (it == null) {
@ -88,12 +90,17 @@ class DramaSeeProvider : MainAPI() {
val poster = fixUrlNull(inner?.select("div.img > img")?.attr("src")) ?: "" val poster = fixUrlNull(inner?.select("div.img > img")?.attr("src")) ?: ""
//Log.i(this.name, "Result => (imgLinkCode) ${imgLinkCode}") //Log.i(this.name, "Result => (imgLinkCode) ${imgLinkCode}")
val title = inner?.select("h1.series-name")?.text() ?: "" val title = inner?.select("h1.series-name")?.text() ?: ""
val year = if (title.length > 5) { title.substring(title.length - 5) val year = if (title.length > 5) {
.trim().trimEnd(')').toIntOrNull() } else { null } title.substring(title.length - 5)
.trim().trimEnd(')').toIntOrNull()
} else {
null
}
//Log.i(this.name, "Result => (year) ${title.substring(title.length - 5)}") //Log.i(this.name, "Result => (year) ${title.substring(title.length - 5)}")
val seriesBody = body?.select("div.series-body") val seriesBody = body?.select("div.series-body")
val descript = seriesBody?.firstOrNull()?.select("div.js-content")?.text() 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 recs = body?.select("ul.series > li")?.mapNotNull {
val a = it.select("a.series-img") ?: return@mapNotNull null val a = it.select("a.series-img") ?: return@mapNotNull null
val aUrl = fixUrlNull(a.attr("href")) ?: 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}") //Log.i(this.name, "Result => (url) ${url}")
when { when {
url.startsWith("https://asianembed.io") || url.startsWith("https://asianload.io") -> { 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) AsianEmbedHelper.getUrls(url, callback)
} }
url.startsWith("https://embedsito.com") -> { url.startsWith("https://embedsito.com") -> {

View file

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.movieproviders package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream
import com.lagradost.cloudstream3.extractors.Vidstream import com.lagradost.cloudstream3.extractors.Vidstream
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.getQualityFromName
@ -14,6 +15,14 @@ open class VidstreamProviderTemplate : MainAPI() {
open val homePageUrlList = listOf<String>() open val homePageUrlList = listOf<String>()
open val vidstreamExtractorUrl: String? = null 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. // // mainUrl is good to have as a holder for the url to make future changes easier.
// override val mainUrl: String // override val mainUrl: String
// get() = "https://vidembed.cc" // get() = "https://vidembed.cc"
@ -212,6 +221,7 @@ open class VidstreamProviderTemplate : MainAPI() {
val iframeLink = val iframeLink =
Jsoup.parse(app.get(data).text).selectFirst("iframe")?.attr("src") ?: return false 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. // 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. // 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) val vidstreamObject = Vidstream(vidstreamExtractorUrl ?: mainUrl)

View file

@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream
import com.lagradost.cloudstream3.extractors.XStreamCdn import com.lagradost.cloudstream3.extractors.XStreamCdn
import com.lagradost.cloudstream3.extractors.helper.AsianEmbedHelper import com.lagradost.cloudstream3.extractors.helper.AsianEmbedHelper
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
@ -35,32 +36,35 @@ class WatchAsianProvider : MainAPI() {
return HomePageResponse( return HomePageResponse(
rowPair.mapNotNull { row -> rowPair.mapNotNull { row ->
val main = (doc.select("div.tab-content.${row.second}") val main = (doc.select("div.tab-content.${row.second}")
?: doc.select("div.tab-content.${row.second}.selected")) ?: return@mapNotNull null ?: doc.select("div.tab-content.${row.second}.selected"))
?: return@mapNotNull null
val title = row.first val title = row.first
val inner = main.select("li") ?: return@mapNotNull null val inner = main.select("li") ?: return@mapNotNull null
HomePageList( HomePageList(
title, title,
inner.map { inner.map {
// Get inner div from article // Get inner div from article
val innerBody = it?.selectFirst("a") val innerBody = it?.selectFirst("a")
// Fetch details // Fetch details
val link = fixUrlNull(innerBody?.attr("href")) ?: return@map null val link = fixUrlNull(innerBody?.attr("href")) ?: return@map null
val image = fixUrlNull(innerBody?.select("img")?.attr("data-original")) ?: "" val image =
val name = (innerBody?.selectFirst("h3.title")?.text() ?: innerBody?.text())?: "<Untitled>" fixUrlNull(innerBody?.select("img")?.attr("data-original")) ?: ""
//Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}") val name = (innerBody?.selectFirst("h3.title")?.text() ?: innerBody?.text())
MovieSearchResponse( ?: "<Untitled>"
name, //Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}")
link, MovieSearchResponse(
this.name, name,
TvType.TvSeries, link,
image, this.name,
year = null, TvType.TvSeries,
id = null, image,
) year = null,
}.filterNotNull().distinctBy { c -> c.url }) id = null,
)
}.filterNotNull().distinctBy { c -> c.url })
}.filter { a -> a.list.isNotEmpty() } }.filter { a -> a.list.isNotEmpty() }
) )
} }
@ -68,13 +72,15 @@ class WatchAsianProvider : MainAPI() {
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/search?type=movies&keyword=$query" val url = "$mainUrl/search?type=movies&keyword=$query"
val document = app.get(url).document.getElementsByTag("body") 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 { return document.mapNotNull {
val innerA = it?.selectFirst("a") ?: return@mapNotNull null val innerA = it?.selectFirst("a") ?: return@mapNotNull null
val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null
val title = it.select("h3.title")?.text() ?: 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 year = null
val imgsrc = innerA.select("img")?.attr("data-original") ?: return@mapNotNull null val imgsrc = innerA.select("img")?.attr("data-original") ?: return@mapNotNull null
val image = fixUrlNull(imgsrc) val image = fixUrlNull(imgsrc)
@ -96,9 +102,9 @@ class WatchAsianProvider : MainAPI() {
val isDramaDetail = url.contains("/drama-detail/") val isDramaDetail = url.contains("/drama-detail/")
var poster = "" var poster = ""
var title = "" var title = ""
var descript : String? = null var descript: String? = null
var year : Int? = null var year: Int? = null
var tags : List<String>? = null var tags: List<String>? = null
if (isDramaDetail) { if (isDramaDetail) {
val main = body.select("div.details") val main = body.select("div.details")
val inner = main?.select("div.info") val inner = main?.select("div.info")
@ -110,7 +116,9 @@ class WatchAsianProvider : MainAPI() {
descript = inner?.text() descript = inner?.text()
inner?.select("p")?.forEach { p -> 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) { when (caption) {
"genre" -> { "genre" -> {
tags = p.select("a")?.mapNotNull { it?.text()?.trim() } tags = p.select("a")?.mapNotNull { it?.text()?.trim() }
@ -132,7 +140,9 @@ class WatchAsianProvider : MainAPI() {
year = if (title.length > 5) { year = if (title.length > 5) {
title.replace(")", "").replace("(", "").substring(title.length - 5) title.replace(")", "").replace("(", "").substring(title.length - 5)
.trim().trimEnd(')').toIntOrNull() .trim().trimEnd(')').toIntOrNull()
} else { null } } else {
null
}
} }
// Episodes Links // Episodes Links
@ -194,7 +204,9 @@ class WatchAsianProvider : MainAPI() {
): Boolean { ): Boolean {
val links = if (data.startsWith(mainUrl)) { val links = if (data.startsWith(mainUrl)) {
getServerLinks(data) getServerLinks(data)
} else { data } } else {
data
}
var count = 0 var count = 0
mapper.readValue<List<String>>(links).apmap { item -> mapper.readValue<List<String>>(links).apmap { item ->
count++ count++
@ -202,6 +214,9 @@ class WatchAsianProvider : MainAPI() {
//Log.i(this.name, "Result => (url) $url") //Log.i(this.name, "Result => (url) $url")
when { when {
url.startsWith("https://asianembed.io") || url.startsWith("https://asianload.io") -> { 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) AsianEmbedHelper.getUrls(url, callback)
} }
url.startsWith("https://embedsito.com") -> { url.startsWith("https://embedsito.com") -> {
@ -219,7 +234,7 @@ class WatchAsianProvider : MainAPI() {
return count > 0 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 val moviedoc = app.get(url, referer = mainUrl).document
return moviedoc.select("div.anime_muti_link > ul > li") return moviedoc.select("div.anime_muti_link > ul > li")
?.mapNotNull { ?.mapNotNull {