From 12de92455960f012bb0298723127061b90932327 Mon Sep 17 00:00:00 2001 From: RowdyRushya Date: Fri, 19 Jul 2024 09:10:34 -0700 Subject: [PATCH] updating vidplay encryption method (#1202) --- .../cloudstream3/extractors/VidSrcTo.kt | 2 +- .../cloudstream3/extractors/Vidplay.kt | 101 ++++++++---------- 2 files changed, 43 insertions(+), 60 deletions(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/VidSrcTo.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/VidSrcTo.kt index 578f5fb9..e974f23a 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/VidSrcTo.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/VidSrcTo.kt @@ -40,7 +40,7 @@ class VidSrcTo : ExtractorApi() { val finalUrl = DecryptUrl(embedRes.result.encUrl) if(finalUrl.equals(embedRes.result.encUrl)) return@amap when (source.title) { - "Vidplay" -> AnyVidplay(finalUrl.substringBefore("/e/")).getUrl(finalUrl, referer, subtitleCallback, callback) + "F2Cloud" -> AnyVidplay(finalUrl.substringBefore("/e/")).getUrl(finalUrl, referer, subtitleCallback, callback) "Filemoon" -> FileMoon().getUrl(finalUrl, referer, subtitleCallback, callback) } } catch (e: Exception) { diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidplay.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidplay.kt index 6202800f..d7e7ce18 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidplay.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidplay.kt @@ -4,13 +4,14 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.base64Encode +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper +import java.net.URLDecoder import javax.crypto.Cipher import javax.crypto.spec.SecretKeySpec -import kotlin.run +import kotlin.io.encoding.Base64 // Code found in https://github.com/KillerDogeEmpire/vidplay-keys // special credits to @KillerDogeEmpire for providing key @@ -33,6 +34,7 @@ class VidplayOnline : Vidplay() { override val mainUrl = "https://vidplay.online" } +@OptIn(kotlin.io.encoding.ExperimentalEncodingApi::class) open class Vidplay : ExtractorApi() { override val name = "Vidplay" override val mainUrl = "https://vidplay.site" @@ -58,83 +60,64 @@ open class Vidplay : ExtractorApi() { } override suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit ) { + val myKeys = getKeys() + val domain = url.substringBefore("/e/") val id = url.substringBefore("?").substringAfterLast("/") - val encodeId = encodeId(id, getKeys()) - val mediaUrl = callFutoken(encodeId, url) - val res = app.get( - "$mediaUrl", headers = mapOf( - "Accept" to "application/json, text/javascript, */*; q=0.01", - "X-Requested-With" to "XMLHttpRequest", - ), referer = url - ).parsedSafe()?.result - + val encodedId = encode(id, myKeys.get(0)) + val t = url.substringAfter("t=").substringBefore("&") + val h = encode(id, myKeys.get(1)) + val mediaUrl = "$domain/mediainfo/$encodedId?t=$t&h=$h" + val encodedRes = + app.get("$mediaUrl").parsedSafe()?.result + ?: throw Exception("Unable to fetch link") + val decodedRes = decode(encodedRes, myKeys.get(2)) + val res = tryParseJson(decodedRes) res?.sources?.map { - M3u8Helper.generateM3u8( - this.name, - it.file ?: return@map, - "$mainUrl/" - ).forEach(callback) + M3u8Helper.generateM3u8(this.name, it.file ?: return@map, "$mainUrl/").forEach(callback) } res?.tracks?.filter { it.kind == "captions" }?.map { - subtitleCallback.invoke( - SubtitleFile(it.label ?: return@map, it.file ?: return@map) - ) + subtitleCallback.invoke(SubtitleFile(it.label ?: return@map, it.file ?: return@map)) } - } - private suspend fun callFutoken(id: String, url: String): String? { - val script = app.get("$mainUrl/futoken", referer = url).text - val k = "k='(\\S+)'".toRegex().find(script)?.groupValues?.get(1) ?: return null - val a = mutableListOf(k) - for (i in id.indices) { - a.add((k[i % k.length].code + id[i].code).toString()) - } - return "$mainUrl/mediainfo/${a.joinToString(",")}?${url.substringAfter("?")}" + private fun encode(input: String, key: String): String { + val rc4Key = SecretKeySpec(key.toByteArray(Charsets.UTF_8), "RC4") + val cipher = Cipher.getInstance("RC4") + cipher.init(Cipher.ENCRYPT_MODE, rc4Key) + val encryptedBytes = cipher.doFinal(input.toByteArray(Charsets.UTF_8)) + return Base64.UrlSafe.encode(encryptedBytes) } - private fun encodeId(id: String, keyList: List): String { - val cipher1 = Cipher.getInstance("RC4") - val cipher2 = Cipher.getInstance("RC4") - cipher1.init( - Cipher.DECRYPT_MODE, - SecretKeySpec(keyList[0].toByteArray(), "RC4"), - cipher1.parameters - ) - cipher2.init( - Cipher.DECRYPT_MODE, - SecretKeySpec(keyList[1].toByteArray(), "RC4"), - cipher2.parameters - ) - var input = id.toByteArray() - input = cipher1.doFinal(input) - input = cipher2.doFinal(input) - return base64Encode(input).replace("/", "_") + fun decode(input: String, key: String): String { + val decodedBytes = Base64.UrlSafe.decode(input) + val rc4Key = SecretKeySpec(key.toByteArray(Charsets.UTF_8), "RC4") + val cipher = Cipher.getInstance("RC4") + cipher.init(Cipher.DECRYPT_MODE, rc4Key) + val decryptedBytes = cipher.doFinal(decodedBytes) + val decodedString = String(decryptedBytes, Charsets.UTF_8) + return URLDecoder.decode(decodedString, "UTF-8") } data class Tracks( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, - @JsonProperty("kind") val kind: String? = null, + @JsonProperty("file") val file: String? = null, + @JsonProperty("label") val label: String? = null, + @JsonProperty("kind") val kind: String? = null, ) data class Sources( - @JsonProperty("file") val file: String? = null, + @JsonProperty("file") val file: String? = null, ) data class Result( - @JsonProperty("sources") val sources: ArrayList? = arrayListOf(), - @JsonProperty("tracks") val tracks: ArrayList? = arrayListOf(), - ) - - data class Response( - @JsonProperty("result") val result: Result? = null, + @JsonProperty("sources") val sources: ArrayList? = arrayListOf(), + @JsonProperty("tracks") val tracks: ArrayList? = arrayListOf(), ) + data class Response(@JsonProperty("result") val result: String? = null) }