Skip to content

Commit 0c1aa21

Browse files
errorcodeQQerrorcodeQQ
authored andcommitted
Fix UI crash by adding try-catch to preferences and network
1 parent 3713b1c commit 0c1aa21

8 files changed

Lines changed: 1121 additions & 0 deletions

File tree

SKTechProvider/build.gradle.kts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
version = 1
2+
3+
cloudstream {
4+
language = "ta"
5+
description = "Watch Live sports and TV channels via SK Tech"
6+
authors = listOf("PluginDeveloper")
7+
status = 1
8+
tvTypes = listOf("Live")
9+
requiresResources = false
10+
isCrossPlatform = false
11+
iconUrl = "https://appteka.store/get/a20p17EuCKFD08AaFsfH3JSJ6OU30ccp18WrKaNDze4LqOsDArhKj13-f-SjAayiWJH6P6pv9AlzhcfzEXlmRokU-I4=/726bbb91e36da425f55efce72cccb3a82df1d265.png"
12+
}
13+
14+
dependencies {
15+
testImplementation("junit:junit:4.13.2")
16+
}
17+
18+
android {
19+
namespace = "com.sktech.plugin"
20+
buildFeatures {
21+
buildConfig = true
22+
viewBinding = false
23+
}
24+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2+
package="com.sktech.plugin" />
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.sktech.plugin
2+
3+
import com.lagradost.cloudstream3.app
4+
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
5+
import java.util.UUID
6+
7+
object FirebaseRemoteConfigFetcher {
8+
private const val PACKAGE_NAME = "com.live.sktechtv"
9+
private const val API_KEY = "AIzaSyClGjK1EBL-ZLbCoep1z5QSmwMyHshimSk"
10+
private const val APP_ID = "1:330162934410:android:0d81c4732e3d206d6cd373"
11+
private const val PROJECT_NUMBER = "330162934410"
12+
13+
data class RemoteConfigResponse(
14+
val entries: Map<String, String>? = null,
15+
val appName: String? = null,
16+
val state: String? = null,
17+
val templateVersion: String? = null
18+
)
19+
20+
suspend fun fetchRemoteConfig(): Map<String, String>? {
21+
return try {
22+
val url = "https://firebaseremoteconfig.googleapis.com/v1/projects/$PROJECT_NUMBER/namespaces/firebase:fetch"
23+
val appInstanceId = UUID.randomUUID().toString().replace("-", "")
24+
25+
val requestBody = mapOf(
26+
"appInstanceId" to appInstanceId,
27+
"appInstanceIdToken" to "",
28+
"appId" to APP_ID,
29+
"countryCode" to "US",
30+
"languageCode" to "en-US",
31+
"platformVersion" to "30",
32+
"timeZone" to "UTC",
33+
"appVersion" to "5.0",
34+
"appBuild" to "50",
35+
"packageName" to PACKAGE_NAME,
36+
"sdkVersion" to "22.1.0",
37+
"analyticsUserProperties" to emptyMap<String, String>()
38+
)
39+
40+
val response = app.post(
41+
url,
42+
headers = mapOf(
43+
"Content-Type" to "application/json",
44+
"Accept" to "application/json",
45+
"X-Android-Package" to PACKAGE_NAME,
46+
"X-Goog-Api-Key" to API_KEY,
47+
"X-Google-GFE-Can-Retry" to "yes"
48+
),
49+
json = requestBody
50+
)
51+
52+
if (response.code == 200) {
53+
val body = response.text
54+
if (body.isNotBlank()) {
55+
return parseJson<RemoteConfigResponse>(body).entries
56+
}
57+
}
58+
null
59+
} catch (_: Exception) {
60+
null
61+
}
62+
}
63+
64+
suspend fun getBaseApiUrl(): String? {
65+
return fetchRemoteConfig()?.get("api_url")?.trimEnd('/')
66+
}
67+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.sktech.plugin
2+
3+
import com.lagradost.cloudstream3.CloudStreamApp.Companion.getKey
4+
import com.lagradost.cloudstream3.CloudStreamApp.Companion.setKey
5+
import kotlinx.coroutines.withTimeoutOrNull
6+
7+
object ProviderManager {
8+
private const val DEFAULT_BASE_URL = "https://nobardis.space"
9+
private var cachedBaseUrl: String? = null
10+
11+
suspend fun getBaseUrl(): String {
12+
cachedBaseUrl?.let { return it }
13+
14+
val saved = try { getKey<String>("SKTECH_CACHED_BASE_URL") } catch (e: Exception) { null }
15+
if (!saved.isNullOrBlank()) {
16+
cachedBaseUrl = saved
17+
return saved
18+
}
19+
20+
val firebaseUrl = try {
21+
withTimeoutOrNull(2000L) {
22+
FirebaseRemoteConfigFetcher.getBaseApiUrl()
23+
}
24+
} catch (_: Exception) { null }
25+
26+
val finalUrl = if (!firebaseUrl.isNullOrBlank()) firebaseUrl else DEFAULT_BASE_URL
27+
cachedBaseUrl = finalUrl
28+
setKey("SKTECH_CACHED_BASE_URL", finalUrl)
29+
return finalUrl
30+
}
31+
32+
fun resetCache() {
33+
cachedBaseUrl = null
34+
setKey("SKTECH_CACHED_BASE_URL", null)
35+
}
36+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.sktech.plugin
2+
3+
import java.nio.charset.StandardCharsets
4+
import javax.crypto.Cipher
5+
import javax.crypto.spec.IvParameterSpec
6+
import javax.crypto.spec.SecretKeySpec
7+
8+
object SKLiveCryptoUtils {
9+
private val AES_KEY = "ST00ZGt3UGlPdVJP".toByteArray(StandardCharsets.UTF_8)
10+
private val AES_IV = "V9LQR42pNKc7smaX".toByteArray(StandardCharsets.UTF_8)
11+
private const val SUFFIX = "BA@GBA@GBA@GBA@G"
12+
13+
private fun swapPairs(data: ByteArray): ByteArray {
14+
val swapped = data.clone()
15+
for (i in 0 until swapped.size - 1 step 2) {
16+
val temp = swapped[i]
17+
swapped[i] = swapped[i + 1]
18+
swapped[i + 1] = temp
19+
}
20+
return swapped
21+
}
22+
23+
private fun safeBase64Decode(input: String): ByteArray {
24+
val cleaned = input.filter { it in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" }.trimEnd('=')
25+
val padded = cleaned + "=".repeat((4 - cleaned.length % 4) % 4)
26+
return java.util.Base64.getDecoder().decode(padded)
27+
}
28+
29+
private fun preprocess(raw: String): String {
30+
return try {
31+
val chars = raw.toCharArray()
32+
for (i in 0 until chars.size - 1 step 2) {
33+
val temp = chars[i]
34+
chars[i] = chars[i + 1]
35+
chars[i + 1] = temp
36+
}
37+
val reversed = String(chars).reversed()
38+
val decoded = String(safeBase64Decode(reversed), StandardCharsets.UTF_8)
39+
if (decoded.endsWith(SUFFIX)) {
40+
decoded.substring(0, decoded.length - SUFFIX.length)
41+
} else {
42+
raw
43+
}
44+
} catch (e: Exception) {
45+
raw
46+
}
47+
}
48+
49+
private fun prepareCiphertext(enc: String): ByteArray? {
50+
return try {
51+
val src = if (enc.startsWith("==")) enc.reversed() else enc
52+
val decoded = safeBase64Decode(src)
53+
if (decoded.size > 12 && decoded.size % 16 == 12) {
54+
decoded.copyOfRange(12, decoded.size)
55+
} else if (decoded.size % 16 == 0) {
56+
decoded
57+
} else {
58+
null
59+
}
60+
} catch (e: Exception) {
61+
null
62+
}
63+
}
64+
65+
private fun decryptAesCbc(ct: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
66+
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
67+
val keySpec = SecretKeySpec(key, "AES")
68+
val ivSpec = IvParameterSpec(iv)
69+
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec)
70+
return cipher.doFinal(ct)
71+
}
72+
73+
fun decryptSKLive(encryptedData: String): String? {
74+
return try {
75+
val preprocessed = preprocess(encryptedData.trim())
76+
val ct = prepareCiphertext(preprocessed) ?: return null
77+
val decryptedBytes = decryptAesCbc(ct, AES_KEY, AES_IV)
78+
79+
val postSwapped = swapPairs(decryptedBytes)
80+
val reversedBytes = postSwapped.reversedArray()
81+
val base64Str = String(reversedBytes, StandardCharsets.UTF_8)
82+
83+
String(safeBase64Decode(base64Str), StandardCharsets.UTF_8)
84+
} catch (e: Exception) {
85+
null
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)