cs-darkdemon-extensions/Bolly2TollyProvider/src/main/kotlin/com/darkdemon/NeoHD.kt

146 lines
4.9 KiB
Kotlin

package com.darkdemon
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64DecodeArray
import com.lagradost.cloudstream3.base64Encode
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import java.security.DigestException
import java.security.MessageDigest
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
class NinjaHD : NeoHD() {
override var name = "NinjaHD"
override var mainUrl = "https://ninjahd.one"
}
open class NeoHD : ExtractorApi() {
override val name = "NeoHD"
override val mainUrl = "https://neohd.xyz"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
val document = app.get(url).text
val cryptoRegex = Regex("""var\s*playerConfig\s*=\s*([^;]+)""")
val json = cryptoRegex.find(document)?.groupValues?.getOrNull(1).toString()
val password = "F1r3b4Ll_GDP~5H".toByteArray()
val data1 = parseJson<AesData>(json)
val decryptedData =
cryptoAESHandler(data1, password, false)?.replace("\\", "")?.substringAfter("\"")
?.substringBeforeLast("\"")
val apiQuery = parseJson<CryptoResponse>(decryptedData!!).apiQuery
val doc = app.get(
url = "https://ninjahd.one/api/?$apiQuery&_=${System.currentTimeMillis() * 1000}",
headers = mapOf(
"X-Requested-With" to "XMLHttpRequest",
"Referer" to "https://ninjahd.one/embed/zilnv7x6da1s84"
)
).text
val source = parseJson<VideoUrl>(doc).sources[0].file
sources.add(
ExtractorLink(
name,
name,
source,
"$mainUrl/",
Qualities.Unknown.value,
headers = mapOf("range" to "bytes=0-")
)
)
return sources
}
private fun GenerateKeyAndIv(
password: ByteArray,
salt: ByteArray,
hashAlgorithm: String = "MD5",
keyLength: Int = 32,
ivLength: Int = 16,
iterations: Int = 1
): List<ByteArray>? {
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 String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}
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()))
}
}
data class AesData(
@JsonProperty("ct") var ct: String,
@JsonProperty("iv") var iv: String,
@JsonProperty("s") var s: String
)
data class CryptoResponse(
@JsonProperty("apiQuery") var apiQuery: String
)
data class VideoUrl(
@JsonProperty("sources") var sources: ArrayList<Sources> = arrayListOf(),
)
data class Sources(
@JsonProperty("file") var file: String
)
}