cloudstream-extensions-hexated/SoraStream/src/main/kotlin/com/hexated/Extractors.kt

190 lines
5.7 KiB
Kotlin

package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.extractors.StreamSB
import com.lagradost.cloudstream3.extractors.XStreamCdn
import com.lagradost.cloudstream3.extractors.helper.AsianEmbedHelper
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec
class StreamM4u : XStreamCdn() {
override val name: String = "StreamM4u"
override val mainUrl: String = "https://streamm4u.club"
}
class Fembed9hd : XStreamCdn() {
override var mainUrl = "https://fembed9hd.com"
override var name = "Fembed9hd"
}
class Sbasian : StreamSB() {
override var mainUrl = "https://sbasian.pro"
override var name = "Sbasian"
}
class Sbnet : StreamSB() {
override var name = "Sbnet"
override var mainUrl = "https://sbnet.one"
}
class Sblongvu : StreamSB() {
override var name = "Sblongvu"
override var mainUrl = "https://sblongvu.com"
}
class Keephealth : StreamSB() {
override var name = "Keephealth"
override var mainUrl = "https://keephealth.info"
}
class Moviesm4u : Filesim() {
override val mainUrl = "https://moviesm4u.com"
override val name = "Moviesm4u"
}
class FileMoonIn : Filesim() {
override val mainUrl = "https://filemoon.in"
override val name = "FileMoon"
}
class StreamhideCom : Filesim() {
override var name: String = "Streamhide"
override var mainUrl: String = "https://streamhide.com"
}
class Movhide : Filesim() {
override var name: String = "Movhide"
override var mainUrl: String = "https://movhide.pro"
}
class Bestx : Chillx() {
override val name = "Bestx"
override val mainUrl = "https://bestx.stream"
}
class Watchx : Chillx() {
override val name = "Watchx"
override val mainUrl = "https://watchx.top"
}
open class Chillx : ExtractorApi() {
override val name = "Chillx"
override val mainUrl = "https://chillx.top"
override val requiresReferer = true
companion object {
private const val KEY = "4VqE3#N7zt&HEP^a"
}
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val master = Regex("MasterJS\\s*=\\s*'([^']+)").find(
app.get(
url,
referer = referer
).text
)?.groupValues?.get(1)
val encData = AppUtils.tryParseJson<AESData>(base64Decode(master ?: return))
val decrypt = cryptoAESHandler(encData ?: return, KEY, false)
val source = Regex("""sources:\s*\[\{"file":"([^"]+)""").find(decrypt)?.groupValues?.get(1)
val tracks = Regex("""tracks:\s*\[(.+)]""").find(decrypt)?.groupValues?.get(1)
// required
val headers = mapOf(
"Accept" to "*/*",
"Connection" to "keep-alive",
"Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "cross-site",
"Origin" to mainUrl,
)
callback.invoke(
ExtractorLink(
name,
name,
source ?: return,
"$mainUrl/",
Qualities.P1080.value,
headers = headers,
isM3u8 = true
)
)
AppUtils.tryParseJson<List<Tracks>>("[$tracks]")
?.filter { it.kind == "captions" }?.map { track ->
subtitleCallback.invoke(
SubtitleFile(
track.label ?: "",
track.file ?: return@map null
)
)
}
}
private fun cryptoAESHandler(
data: AESData,
pass: String,
encrypt: Boolean = true
): String {
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512")
val spec = PBEKeySpec(
pass.toCharArray(),
data.salt?.hexToByteArray(),
data.iterations?.toIntOrNull() ?: 1,
256
)
val key = factory.generateSecret(spec)
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
return if (!encrypt) {
cipher.init(
Cipher.DECRYPT_MODE,
SecretKeySpec(key.encoded, "AES"),
IvParameterSpec(data.iv?.hexToByteArray())
)
String(cipher.doFinal(base64DecodeArray(data.ciphertext.toString())))
} else {
cipher.init(
Cipher.ENCRYPT_MODE,
SecretKeySpec(key.encoded, "AES"),
IvParameterSpec(data.iv?.hexToByteArray())
)
base64Encode(cipher.doFinal(data.ciphertext?.toByteArray()))
}
}
private fun String.hexToByteArray(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}
data class AESData(
@JsonProperty("ciphertext") val ciphertext: String? = null,
@JsonProperty("iv") val iv: String? = null,
@JsonProperty("salt") val salt: String? = null,
@JsonProperty("iterations") val iterations: String? = null,
)
data class Tracks(
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,
@JsonProperty("kind") val kind: String? = null,
)
}