update vidplay encryption (#1285)

This commit is contained in:
RowdyRushya
2024-08-19 10:58:52 -07:00
committed by GitHub
parent 711cc687ac
commit adb8dc26ea

View File

@ -36,88 +36,132 @@ class VidplayOnline : Vidplay() {
@OptIn(kotlin.io.encoding.ExperimentalEncodingApi::class) @OptIn(kotlin.io.encoding.ExperimentalEncodingApi::class)
open class Vidplay : ExtractorApi() { open class Vidplay : ExtractorApi() {
override val name = "Vidplay" override val name = "Vidplay"
override val mainUrl = "https://vidplay.site" override val mainUrl = "https://vidplay.site"
override val requiresReferer = true override val requiresReferer = true
companion object { companion object {
private val keySource = "https://rowdy-avocado.github.io/multi-keys/" private val keySource = "https://rowdy-avocado.github.io/multi-keys/"
private var keys: List<String>? = null private var keys: KeysData? = null
private suspend fun getKeys(): List<String> { private suspend fun getKeys(): KeysData {
return keys return keys
?: run { ?: run {
val res = keys = app.get(keySource).parsedSafe<KeysData>()
app.get(keySource).parsedSafe<KeysData>() ?: throw ErrorLoadingException("Unable to get keys")
?: throw ErrorLoadingException("Unable to get keys") keys!!
keys = res.keys }
res.keys }
} }
}
private data class KeysData(@JsonProperty("vidplay") val keys: List<String>) override suspend fun getUrl(
} url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val myKeys = getKeys()
val domain = url.substringBefore("/e/")
val id = url.substringBefore("?").substringAfterLast("/")
val encodedId = vrfEncrypt(myKeys, id)
val t = url.substringAfter("t=").substringBefore("&")
val h = rc4Encryption(myKeys.vidplay.find { it.method == "h" }?.keys?.get(0) ?: return, id)
val mediaUrl = "$domain/mediainfo/$encodedId?t=$t&h=$h"
val encodedRes = app.get("$mediaUrl").parsedSafe<Response>()?.result ?: throw Exception("Unable to fetch link")
val decodedRes = vrfDecrypt(myKeys, encodedRes)
val res = tryParseJson<Result>(decodedRes)
res?.sources?.map {
M3u8Helper.generateM3u8(this.name, it.file ?: return@map, "$mainUrl/").forEach(callback)
}
override suspend fun getUrl( res?.tracks?.filter { it.kind == "captions" }?.map {
url: String, subtitleCallback.invoke(SubtitleFile(it.label ?: return@map, it.file ?: return@map))
referer: String?, }
subtitleCallback: (SubtitleFile) -> Unit, }
callback: (ExtractorLink) -> Unit
) {
val myKeys = getKeys()
val domain = url.substringBefore("/e/")
val id = url.substringBefore("?").substringAfterLast("/")
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<Response>()?.result
?: throw Exception("Unable to fetch link")
val decodedRes = decode(encodedRes, myKeys.get(2))
val res = tryParseJson<Result>(decodedRes)
res?.sources?.map {
M3u8Helper.generateM3u8(this.name, it.file ?: return@map, "$mainUrl/").forEach(callback)
}
res?.tracks?.filter { it.kind == "captions" }?.map { private fun vrfEncrypt(keys: KeysData, input: String): String? {
subtitleCallback.invoke(SubtitleFile(it.label ?: return@map, it.file ?: return@map)) var vrf = input
} keys.vidplay.sortedBy { it.sequence }.forEach { step ->
} when(step.method) {
"exchange" -> vrf = exchange(vrf, step.keys?.get(0) ?: return@forEach, step.keys?.get(1) ?: return@forEach)
"rc4" -> vrf = rc4Encryption(step.keys?.get(0) ?: return@forEach, vrf)
"reverse" -> vrf = vrf.reversed()
"base64" -> vrf = Base64.UrlSafe.encode(vrf.toByteArray())
"else" -> {}
}
}
// vrf = java.net.URLEncoder.encode(vrf, "UTF-8")
return vrf
}
private fun encode(input: String, key: String): String { private fun vrfDecrypt(keys: KeysData, input: String): String {
val rc4Key = SecretKeySpec(key.toByteArray(Charsets.UTF_8), "RC4") var vrf = input
val cipher = Cipher.getInstance("RC4") keys.vidplay.sortedByDescending { it.sequence }.forEach { step ->
cipher.init(Cipher.ENCRYPT_MODE, rc4Key) when(step.method) {
val encryptedBytes = cipher.doFinal(input.toByteArray(Charsets.UTF_8)) "exchange" -> vrf = exchange(vrf, step.keys?.get(1) ?: return@forEach, step.keys?.get(0) ?: return@forEach)
return Base64.UrlSafe.encode(encryptedBytes) "rc4" -> vrf = rc4Decryption(step.keys?.get(0) ?: return@forEach, vrf)
} "reverse" -> vrf = vrf.reversed()
"base64" -> vrf = Base64.UrlSafe.decode(vrf).toString(Charsets.UTF_8)
"else" -> {}
}
}
return URLDecoder.decode(vrf, "utf-8")
}
fun decode(input: String, key: String): String { private fun rc4Encryption(key: String, input: String): String {
val decodedBytes = Base64.UrlSafe.decode(input) val rc4Key = SecretKeySpec(key.toByteArray(), "RC4")
val rc4Key = SecretKeySpec(key.toByteArray(Charsets.UTF_8), "RC4") val cipher = Cipher.getInstance("RC4")
val cipher = Cipher.getInstance("RC4") cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
cipher.init(Cipher.DECRYPT_MODE, rc4Key) var output = cipher.doFinal(input.toByteArray())
val decryptedBytes = cipher.doFinal(decodedBytes) output = Base64.UrlSafe.encode(output).toByteArray()
val decodedString = String(decryptedBytes, Charsets.UTF_8) return output.toString(Charsets.UTF_8)
return URLDecoder.decode(decodedString, "UTF-8") }
}
data class Tracks( private fun rc4Decryption(key: String, input: String): String {
@JsonProperty("file") val file: String? = null, var vrf = input.toByteArray()
@JsonProperty("label") val label: String? = null, vrf = Base64.UrlSafe.decode(vrf)
@JsonProperty("kind") val kind: String? = null, val rc4Key = SecretKeySpec(key.toByteArray(), "RC4")
) val cipher = Cipher.getInstance("RC4")
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
vrf = cipher.doFinal(vrf)
data class Sources( return vrf.toString(Charsets.UTF_8)
@JsonProperty("file") val file: String? = null, }
)
private fun exchange(input: String, key1: String, key2: String): String {
return input.map { i ->
val index = key1.indexOf(i)
if (index != -1) {
key2[index]
} else {
i
}
}.joinToString("")
}
data class Result( data class Tracks(
@JsonProperty("sources") val sources: ArrayList<Sources>? = arrayListOf(), @JsonProperty("file") val file: String? = null,
@JsonProperty("tracks") val tracks: ArrayList<Tracks>? = arrayListOf(), @JsonProperty("label") val label: String? = null,
) @JsonProperty("kind") val kind: String? = null,
)
data class Response(@JsonProperty("result") val result: String? = null) data class Sources(
@JsonProperty("file") val file: String? = null,
)
data class Result(
@JsonProperty("sources") val sources: ArrayList<Sources>? = arrayListOf(),
@JsonProperty("tracks") val tracks: ArrayList<Tracks>? = arrayListOf(),
)
data class Response(@JsonProperty("result") val result: String? = null)
data class KeysData(@JsonProperty("vidplay") val vidplay: List<Step>)
data class Step(
@JsonProperty("sequence") val sequence: Int,
@JsonProperty("method") val method: String,
@JsonProperty("keys") val keys: List<String>? = null
)
} }