diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt index c5eaf40e..7ec1fb22 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt @@ -7,6 +7,10 @@ import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.getQualityFromName import kotlinx.coroutines.delay +class DoodWfExtractor : DoodLaExtractor() { + override var mainUrl = "https://dood.wf" +} + class DoodCxExtractor : DoodLaExtractor() { override var mainUrl = "https://dood.cx" } diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt new file mode 100644 index 00000000..d2e56bf1 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt @@ -0,0 +1,178 @@ +package com.lagradost.cloudstream3.extractors + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import org.jsoup.nodes.Element +import java.security.DigestException +import java.security.MessageDigest +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +class Gdriveplayerapi: Gdriveplayer() { + override val mainUrl: String = "https://gdriveplayerapi.com" +} + +class Gdriveplayerapp: Gdriveplayer() { + override val mainUrl: String = "https://gdriveplayer.app" +} + +class Gdriveplayerfun: Gdriveplayer() { + override val mainUrl: String = "https://gdriveplayer.fun" +} + +class Gdriveplayerio: Gdriveplayer() { + override val mainUrl: String = "https://gdriveplayer.io" +} + +class Gdriveplayerme: Gdriveplayer() { + override val mainUrl: String = "https://gdriveplayer.me" +} + +class Gdriveplayerbiz: Gdriveplayer() { + override val mainUrl: String = "https://gdriveplayer.biz" +} + +class Gdriveplayerorg: Gdriveplayer() { + override val mainUrl: String = "https://gdriveplayer.org" +} + +class Gdriveplayerus: Gdriveplayer() { + override val mainUrl: String = "https://gdriveplayer.us" +} + +class Gdriveplayerco: Gdriveplayer() { + override val mainUrl: String = "https://gdriveplayer.co" +} + +open class Gdriveplayer : ExtractorApi() { + override val name = "Gdrive" + override val mainUrl = "https://gdriveplayer.to" + override val requiresReferer = false + + private fun unpackJs(script: Element): String? { + return script.select("script").find { it.data().contains("eval(function(p,a,c,k,e,d)") } + ?.data()?.let { getAndUnpack(it) } + } + + private fun String.decodeHex(): ByteArray { + check(length % 2 == 0) { "Must have an even length" } + return chunked(2) + .map { it.toInt(16).toByte() } + .toByteArray() + } + + // https://stackoverflow.com/a/41434590/8166854 + private fun GenerateKeyAndIv( + password: ByteArray, + salt: ByteArray, + hashAlgorithm: String = "MD5", + keyLength: Int = 32, + ivLength: Int = 16, + iterations: Int = 1 + ): List? { + + val md = MessageDigest.getInstance(hashAlgorithm) + val digestLength = md.digestLength + val targetKeySize = keyLength + ivLength + val requiredLength = (targetKeySize + digestLength - 1) / digestLength * digestLength + val generatedData = ByteArray(requiredLength) + var generatedLength = 0 + + try { + md.reset() + + while (generatedLength < targetKeySize) { + if (generatedLength > 0) + md.update( + generatedData, + generatedLength - digestLength, + digestLength + ) + + md.update(password) + md.update(salt, 0, 8) + md.digest(generatedData, generatedLength, digestLength) + + for (i in 1 until iterations) { + md.update(generatedData, generatedLength, digestLength) + md.digest(generatedData, generatedLength, digestLength) + } + + generatedLength += digestLength + } + return listOf( + generatedData.copyOfRange(0, keyLength), + generatedData.copyOfRange(keyLength, targetKeySize) + ) + } catch (e: DigestException) { + return null + } + } + + private fun cryptoAESHandler( + data: AesData, + pass: ByteArray, + encrypt: Boolean = true + ): String? { + val (key, iv) = GenerateKeyAndIv(pass, data.s.decodeHex()) ?: return null + val cipher = Cipher.getInstance("AES/CBC/NoPadding") + return if (!encrypt) { + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) + String(cipher.doFinal(base64DecodeArray(data.ct))) + } else { + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) + base64Encode(cipher.doFinal(data.ct.toByteArray())) + + } + } + + private fun Regex.first(str: String): String? { + return find(str)?.groupValues?.getOrNull(1) + } + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val document = app.get(url).document + + val eval = unpackJs(document)?.replace("\\", "") ?: return + val data = AppUtils.tryParseJson(Regex("data='(\\S+?)'").first(eval)) ?: return + val password = Regex("null,['|\"](\\w+)['|\"]").first(eval) + ?.split(Regex("\\D+")) + ?.joinToString("") { + Char(it.toInt()).toString() + }.let { Regex("var pass = \"(\\S+?)\"").first(it ?: return)?.toByteArray() } + ?: throw ErrorLoadingException("can't find password") + val decryptedData = + cryptoAESHandler(data, password, false)?.let { getAndUnpack(it) }?.replace("\\", "") + ?.substringAfter("sources:[")?.substringBefore("],") + + Regex("\"file\":\"(\\S+?)\".*?res=(\\d+)").findAll(decryptedData ?: return).map { + it.groupValues[1] to it.groupValues[2] + }.toList().distinctBy { it.second }.map { (link, quality) -> + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = "${httpsify(link)}&res=$quality", + referer = mainUrl, + quality = quality.toIntOrNull() ?: Qualities.Unknown.value, + headers = mapOf("Range" to "bytes=0-") + ) + ) + } + + } + + data class AesData( + @JsonProperty("ct") val ct: String, + @JsonProperty("iv") val iv: String, + @JsonProperty("s") val s: String + ) + +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt index a933c484..461f56d0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt @@ -7,6 +7,11 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper +class Sbflix : StreamSB() { + override var mainUrl = "https://sbflix.xyz" + override var name = "Sbflix" +} + class Vidgomunime : StreamSB() { override var mainUrl = "https://vidgomunime.xyz" } @@ -111,7 +116,7 @@ open class StreamSB : ExtractorApi() { }.first() val bytes = id.toByteArray() val bytesToHex = bytesToHex(bytes) - val master = "$mainUrl/sources43/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362" + val master = "$mainUrl/sources44/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362" val headers = mapOf( "watchsb" to "streamsb", ) 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 7a65df30..ae3d8c06 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -236,6 +236,7 @@ val extractorApis: MutableList = arrayListOf( Ssbstream(), Sbthe(), Vidgomunime(), + Sbflix(), Fastream(), @@ -269,6 +270,7 @@ val extractorApis: MutableList = arrayListOf( DoodWsExtractor(), DoodShExtractor(), DoodWatchExtractor(), + DoodWfExtractor(), AsianLoad(), @@ -321,6 +323,17 @@ val extractorApis: MutableList = arrayListOf( Mvidoo(), Streamplay(), + Gdriveplayerapi(), + Gdriveplayerapp(), + Gdriveplayerfun(), + Gdriveplayerio(), + Gdriveplayerme(), + Gdriveplayerbiz(), + Gdriveplayerorg(), + Gdriveplayerus(), + Gdriveplayerco(), + Gdriveplayer(), + YoutubeExtractor(), YoutubeShortLinkExtractor(), YoutubeMobileExtractor(),