Skip to content

Instantly share code, notes, and snippets.

@ShivamKumarJha
Created October 2, 2025 17:28
Show Gist options
  • Select an option

  • Save ShivamKumarJha/cf84dc1b9a47a02a6526121160b44425 to your computer and use it in GitHub Desktop.

Select an option

Save ShivamKumarJha/cf84dc1b9a47a02a6526121160b44425 to your computer and use it in GitHub Desktop.
EmbedSportsExtractor used streamed.su streamcentre etc
package com.shivamkumarjha.media_extractor.decrypt
import dev.whyoleg.cryptography.CryptographyProvider
import dev.whyoleg.cryptography.DelicateCryptographyApi
import dev.whyoleg.cryptography.algorithms.AES
@OptIn(DelicateCryptographyApi::class)
internal suspend fun aesCbcDecrypt(data: ByteArray, key: ByteArray, iv: ByteArray): String {
val aesCtr = CryptographyProvider.Default.get(AES.CBC)
val key = aesCtr.keyDecoder().decodeFromByteArray(AES.Key.Format.RAW, key)
val cipher = key.cipher()
val decrypted = cipher.decryptWithIv(iv, data)
return decrypted.decodeToString()
}
@OptIn(DelicateCryptographyApi::class)
internal suspend fun aesCtrDecrypt(data: ByteArray, key: ByteArray, iv: ByteArray): String {
val aesCtr = CryptographyProvider.Default.get(AES.CTR)
val key = aesCtr.keyDecoder().decodeFromByteArray(AES.Key.Format.RAW, key)
val cipher = key.cipher()
val decrypted = cipher.decryptWithIv(iv, data)
return decrypted.decodeToString()
}
@OptIn(DelicateCryptographyApi::class)
internal suspend fun aesCbcEncrypt(raw: String, key: ByteArray, iv: ByteArray): ByteArray {
val aes = CryptographyProvider.Default.get(AES.CBC)
val key = aes.keyDecoder().decodeFromByteArray(AES.Key.Format.RAW, key)
val cipher = key.cipher()
val encrypted = cipher.encryptWithIv(iv, raw.encodeToByteArray())
return encrypted
}
fun hexToBytes(hex: String): ByteArray {
return hex.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
}
package com.shivamkumarjha.media_extractor.extractors
import com.shivamkumarjha.media_common.model.Media
import com.shivamkumarjha.media_common.model.MediaPlayerItem
import com.shivamkumarjha.media_common.model.MediaType
import com.shivamkumarjha.media_extractor.BaseMediaExtractor
import com.shivamkumarjha.media_extractor.decrypt.aesCtrDecrypt
import com.shivamkumarjha.network_client.getBaseUrl
import com.shivamkumarjha.network_client.getHost
import com.shivamkumarjha.network_common.HttpRequestData
import com.shivamkumarjha.network_common.Resource
import com.shivamkumarjha.utility_logging.logger
import io.ktor.client.HttpClient
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.readRawBytes
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.contentType
import io.ktor.utils.io.charsets.Charsets
import io.ktor.utils.io.core.toByteArray
import kotlin.io.encoding.Base64
internal class EmbedSportsExtractor(
private val httpClient: HttpClient,
) : BaseMediaExtractor() {
override val tag: String
get() = "EmbedSports"
override suspend fun isSupported(httpRequestData: HttpRequestData): Boolean {
return httpRequestData.url.getHost().contains("embedsports")
}
override suspend fun mediaPlayerItem(
httpRequestData: HttpRequestData,
callback: suspend (MediaPlayerItem) -> Unit
): Resource<MediaPlayerItem> {
val parts = httpRequestData.url.trimEnd('/').split("/")
if (parts.size < 3) {
val throwable = IllegalArgumentException("Invalid URL format")
logger.error("URL parts error.", throwable, tag)
return Resource.Error(null, throwable)
}
val streamSc = parts[parts.size - 3]
val streamId = parts[parts.size - 2]
val streamNo = parts[parts.size - 1]
val payload = buildByteArray(streamSc, streamId, streamNo)
val response = httpClient.post("https://embedsports.top/fetch") {
contentType(ContentType.Application.OctetStream)
setBody(payload)
}
val aesKey = response.headers["What"]?.toByteArray(Charsets.UTF_8)
if (aesKey == null) {
val throwable = Throwable("Missing AES key in response headers")
logger.error("AES Key missing.", throwable, tag)
return Resource.Error(null, throwable)
}
val aesIv = "STOPSTOPSTOPSTOP".toByteArray(Charsets.UTF_8)
val content = response.readRawBytes()
val b64CipherLength = content[1].toInt() and 0xFF
val b64Cipher = content.takeLast(b64CipherLength).toByteArray()
val b64Decipher = b64Cipher.map {
if (it >= 0x50) (it - 47).toByte() else (it + 47).toByte()
}.toByteArray()
val decoded = Base64.decode(b64Decipher)
val decrypted = aesCtrDecrypt(decoded, aesKey, aesIv)
logger.debug("decrypted: $decrypted", tag)
val mediaList = listOf(
Media(
httpRequestData = HttpRequestData(
url = decrypted,
headers = mapOf(
HttpHeaders.Origin to httpRequestData.url.getBaseUrl(),
HttpHeaders.Referrer to httpRequestData.url,
)
),
mediaType = MediaType.LIVE,
label = httpRequestData.url.getHost(),
description = decrypted.getHost(),
)
)
return Resource.Success(MediaPlayerItem(mediaList = mediaList))
}
private fun buildByteArray(sc: String, id: String, no: String): ByteArray {
val payload = mutableListOf<Byte>()
payload.add(0x0A)
payload.add(sc.length.toByte())
payload.addAll(sc.toByteArray().toList())
payload.add(0x12)
payload.add(id.length.toByte())
payload.addAll(id.toByteArray().toList())
payload.add(0x1A)
payload.add(no.length.toByte())
payload.addAll(no.toByteArray().toList())
return payload.toByteArray()
}
}
import io.ktor.http.Url
fun String.getBaseUrl(): String {
return try {
val parsedUrl = Url(this)
"${parsedUrl.protocol.name}://${parsedUrl.host}"
} catch (e: Exception) {
e.printStackTrace()
this
}
}
fun String.getHost(): String {
return try {
Url(this).host
} catch (e: Exception) {
e.printStackTrace()
this
}
}
@Serializable
data class HttpRequestData(
val url: String,
val headers: Map<String, String> = emptyMap(),
val queries: Map<String, String> = emptyMap(),
val body: String? = null,
)
@Serializable
data class Media(
val httpRequestData: HttpRequestData,
val label: String,
val mediaType: MediaType = MediaType.AUTO,
val hlsExtraction: Boolean = true,
val description: String = "",
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment