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

@ -43,20 +43,16 @@ open class Vidplay : ExtractorApi() {
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 = res.keys keys!!
res.keys
} }
} }
private data class KeysData(@JsonProperty("vidplay") val keys: List<String>)
} }
override suspend fun getUrl( override suspend fun getUrl(
@ -68,14 +64,12 @@ open class Vidplay : ExtractorApi() {
val myKeys = getKeys() val myKeys = getKeys()
val domain = url.substringBefore("/e/") val domain = url.substringBefore("/e/")
val id = url.substringBefore("?").substringAfterLast("/") val id = url.substringBefore("?").substringAfterLast("/")
val encodedId = encode(id, myKeys.get(0)) val encodedId = vrfEncrypt(myKeys, id)
val t = url.substringAfter("t=").substringBefore("&") val t = url.substringAfter("t=").substringBefore("&")
val h = encode(id, myKeys.get(1)) val h = rc4Encryption(myKeys.vidplay.find { it.method == "h" }?.keys?.get(0) ?: return, id)
val mediaUrl = "$domain/mediainfo/$encodedId?t=$t&h=$h" val mediaUrl = "$domain/mediainfo/$encodedId?t=$t&h=$h"
val encodedRes = val encodedRes = app.get("$mediaUrl").parsedSafe<Response>()?.result ?: throw Exception("Unable to fetch link")
app.get("$mediaUrl").parsedSafe<Response>()?.result val decodedRes = vrfDecrypt(myKeys, encodedRes)
?: throw Exception("Unable to fetch link")
val decodedRes = decode(encodedRes, myKeys.get(2))
val res = tryParseJson<Result>(decodedRes) val res = tryParseJson<Result>(decodedRes)
res?.sources?.map { 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)
@ -86,22 +80,64 @@ open class Vidplay : ExtractorApi() {
} }
} }
private fun encode(input: String, key: String): String { private fun vrfEncrypt(keys: KeysData, input: String): String? {
val rc4Key = SecretKeySpec(key.toByteArray(Charsets.UTF_8), "RC4") var vrf = input
val cipher = Cipher.getInstance("RC4") keys.vidplay.sortedBy { 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(0) ?: return@forEach, step.keys?.get(1) ?: return@forEach)
return Base64.UrlSafe.encode(encryptedBytes) "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
} }
fun decode(input: String, key: String): String { private fun vrfDecrypt(keys: KeysData, input: String): String {
val decodedBytes = Base64.UrlSafe.decode(input) var vrf = input
val rc4Key = SecretKeySpec(key.toByteArray(Charsets.UTF_8), "RC4") keys.vidplay.sortedByDescending { it.sequence }.forEach { step ->
when(step.method) {
"exchange" -> vrf = exchange(vrf, step.keys?.get(1) ?: return@forEach, step.keys?.get(0) ?: return@forEach)
"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")
}
private fun rc4Encryption(key: String, input: String): String {
val rc4Key = SecretKeySpec(key.toByteArray(), "RC4")
val cipher = Cipher.getInstance("RC4") val cipher = Cipher.getInstance("RC4")
cipher.init(Cipher.DECRYPT_MODE, rc4Key) cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
val decryptedBytes = cipher.doFinal(decodedBytes) var output = cipher.doFinal(input.toByteArray())
val decodedString = String(decryptedBytes, Charsets.UTF_8) output = Base64.UrlSafe.encode(output).toByteArray()
return URLDecoder.decode(decodedString, "UTF-8") return output.toString(Charsets.UTF_8)
}
private fun rc4Decryption(key: String, input: String): String {
var vrf = input.toByteArray()
vrf = Base64.UrlSafe.decode(vrf)
val rc4Key = SecretKeySpec(key.toByteArray(), "RC4")
val cipher = Cipher.getInstance("RC4")
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
vrf = cipher.doFinal(vrf)
return vrf.toString(Charsets.UTF_8)
}
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 Tracks( data class Tracks(
@ -120,4 +156,12 @@ open class Vidplay : ExtractorApi() {
) )
data class Response(@JsonProperty("result") val result: String? = null) 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
)
} }