Created
October 2, 2025 17:28
-
-
Save ShivamKumarJha/cf84dc1b9a47a02a6526121160b44425 to your computer and use it in GitHub Desktop.
EmbedSportsExtractor used streamed.su streamcentre etc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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() | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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() | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @Serializable | |
| data class HttpRequestData( | |
| val url: String, | |
| val headers: Map<String, String> = emptyMap(), | |
| val queries: Map<String, String> = emptyMap(), | |
| val body: String? = null, | |
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @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