From 8dc70c0d0cd02d0d9ef244d0ac9b089d11c7017d Mon Sep 17 00:00:00 2001 From: Secozzi <49240133+Secozzi@users.noreply.github.com> Date: Mon, 15 Jan 2024 21:25:08 +0000 Subject: [PATCH] feat(lib/megacloud): Save key to preferences (#2758) --- .../megacloudextractor/MegaCloudExtractor.kt | 91 +++++++++++-------- .../{kaido => default}/additional.gradle | 0 .../overrides/zorotheme/kaido/src/Kaido.kt | 2 +- .../zorotheme/zoro/additional.gradle | 4 - .../overrides/zorotheme/zoro/src/AniWatch.kt | 2 +- .../tachiyomi/multisrc/zorotheme/ZoroTheme.kt | 30 ++---- 6 files changed, 64 insertions(+), 65 deletions(-) rename multisrc/overrides/zorotheme/{kaido => default}/additional.gradle (100%) delete mode 100644 multisrc/overrides/zorotheme/zoro/additional.gradle diff --git a/lib/megacloud-extractor/src/main/java/eu/kanade/tachiyomi/lib/megacloudextractor/MegaCloudExtractor.kt b/lib/megacloud-extractor/src/main/java/eu/kanade/tachiyomi/lib/megacloudextractor/MegaCloudExtractor.kt index a39578f15..67ffc28e8 100644 --- a/lib/megacloud-extractor/src/main/java/eu/kanade/tachiyomi/lib/megacloudextractor/MegaCloudExtractor.kt +++ b/lib/megacloud-extractor/src/main/java/eu/kanade/tachiyomi/lib/megacloudextractor/MegaCloudExtractor.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.lib.megacloudextractor +import android.content.SharedPreferences import eu.kanade.tachiyomi.animesource.model.Track import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES @@ -10,6 +11,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.jsonPrimitive @@ -19,7 +21,11 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import uy.kohesive.injekt.injectLazy -class MegaCloudExtractor(private val client: OkHttpClient, private val headers: Headers) { +class MegaCloudExtractor( + private val client: OkHttpClient, + private val headers: Headers, + private val preferences: SharedPreferences +) { private val json: Json by injectLazy() private val playlistUtils by lazy { PlaylistUtils(client, headers) } @@ -36,56 +42,65 @@ class MegaCloudExtractor(private val client: OkHttpClient, private val headers: private val SOURCES_KEY = arrayOf("1", "6") private const val E1_SCRIPT_URL = "https://megacloud.tv/js/player/a/prod/e1-player.min.js" private const val E6_SCRIPT_URL = "https://rapid-cloud.co/js/player/prod/e6-player-v2.min.js" - private const val E4_SCRIPT_URL = "https://rabbitstream.net/js/player/prod/e4-player.min.js" - private val INDEX_PAIRS_MAP = mutableMapOf("1" to emptyList>(), "6" to emptyList>()) private val MUTEX = Mutex() + private var shouldUpdateKey = false + private const val PREF_KEY_KEY = "megacloud_key_" + private const val PREF_KEY_DEFAULT = "[[0, 0]]" private inline fun runLocked(crossinline block: () -> R) = runBlocking(Dispatchers.IO) { MUTEX.withLock { block() } } } - private fun getIndexPairs(type: String) = runLocked { - INDEX_PAIRS_MAP[type].orEmpty().ifEmpty { - val scriptUrl = when (type) { - "1" -> E1_SCRIPT_URL - "6" -> E6_SCRIPT_URL - "4" -> E4_SCRIPT_URL - else -> throw Exception("Unknown key type") - } - val script = noCacheClient.newCall(GET(scriptUrl, cache = cacheControl)) - .execute() - .use { it.body.string() } - val regex = - Regex("case\\s*0x[0-9a-f]+:(?![^;]*=partKey)\\s*\\w+\\s*=\\s*(\\w+)\\s*,\\s*\\w+\\s*=\\s*(\\w+);") - val matches = regex.findAll(script).toList() - val indexPairs = matches.map { match -> - val var1 = match.groupValues[1] - val var2 = match.groupValues[2] + // Stolen from TurkAnime + private fun getKey(type: String): List> = runLocked { + if (shouldUpdateKey) { + updateKey(type) + shouldUpdateKey = false + } + json.decodeFromString>>( + preferences.getString(PREF_KEY_KEY + type, PREF_KEY_DEFAULT)!! + ) + } - val regexVar1 = Regex(",${var1}=((?:0x)?([0-9a-fA-F]+))") - val regexVar2 = Regex(",${var2}=((?:0x)?([0-9a-fA-F]+))") + private fun updateKey(type: String) { + val scriptUrl = when (type) { + "1" -> E1_SCRIPT_URL + "6" -> E6_SCRIPT_URL + else -> throw Exception("Unknown key type") + } + val script = noCacheClient.newCall(GET(scriptUrl, cache = cacheControl)) + .execute() + .use { it.body.string() } + val regex = + Regex("case\\s*0x[0-9a-f]+:(?![^;]*=partKey)\\s*\\w+\\s*=\\s*(\\w+)\\s*,\\s*\\w+\\s*=\\s*(\\w+);") + val matches = regex.findAll(script).toList() + val indexPairs = matches.map { match -> + val var1 = match.groupValues[1] + val var2 = match.groupValues[2] - val matchVar1 = regexVar1.find(script)?.groupValues?.get(1)?.removePrefix("0x") - val matchVar2 = regexVar2.find(script)?.groupValues?.get(1)?.removePrefix("0x") + val regexVar1 = Regex(",${var1}=((?:0x)?([0-9a-fA-F]+))") + val regexVar2 = Regex(",${var2}=((?:0x)?([0-9a-fA-F]+))") - if (matchVar1 != null && matchVar2 != null) { - try { - listOf(matchVar1.toInt(16), matchVar2.toInt(16)) - } catch (e: NumberFormatException) { - emptyList() - } - } else { + val matchVar1 = regexVar1.find(script)?.groupValues?.get(1)?.removePrefix("0x") + val matchVar2 = regexVar2.find(script)?.groupValues?.get(1)?.removePrefix("0x") + + if (matchVar1 != null && matchVar2 != null) { + try { + listOf(matchVar1.toInt(16), matchVar2.toInt(16)) + } catch (e: NumberFormatException) { emptyList() } - }.filter { it.isNotEmpty() } - INDEX_PAIRS_MAP[type] = indexPairs - indexPairs - } + } else { + emptyList() + } + }.filter { it.isNotEmpty() } + val encoded = json.encodeToString(indexPairs) + preferences.edit().putString(PREF_KEY_KEY + type, encoded).apply() } private fun cipherTextCleaner(data: String, type: String): Pair { - val indexPairs = getIndexPairs(type) + val indexPairs = getKey(type) val (password, ciphertext, _) = indexPairs.fold(Triple("", data, 0)) { previous, item -> val start = item.first() + previous.third val end = start + item.last() @@ -103,7 +118,7 @@ class MegaCloudExtractor(private val client: OkHttpClient, private val headers: val (ciphertext, password) = cipherTextCleaner(ciphered, type) return CryptoAES.decrypt(ciphertext, password).ifEmpty { // Update index pairs - runLocked { INDEX_PAIRS_MAP[type] = emptyList>() } + shouldUpdateKey = true tryDecrypting(ciphered, type, attempts + 1) } } @@ -138,7 +153,7 @@ class MegaCloudExtractor(private val client: OkHttpClient, private val headers: if (!data.encrypted) return json.decodeFromString(srcRes) - val ciphered = data.sources.jsonPrimitive.content.toString() + val ciphered = data.sources.jsonPrimitive.content val decrypted = json.decodeFromString>(tryDecrypting(ciphered, keyType)) return VideoDto(decrypted, data.tracks) diff --git a/multisrc/overrides/zorotheme/kaido/additional.gradle b/multisrc/overrides/zorotheme/default/additional.gradle similarity index 100% rename from multisrc/overrides/zorotheme/kaido/additional.gradle rename to multisrc/overrides/zorotheme/default/additional.gradle diff --git a/multisrc/overrides/zorotheme/kaido/src/Kaido.kt b/multisrc/overrides/zorotheme/kaido/src/Kaido.kt index f489e4e46..c0456d91c 100644 --- a/multisrc/overrides/zorotheme/kaido/src/Kaido.kt +++ b/multisrc/overrides/zorotheme/kaido/src/Kaido.kt @@ -17,7 +17,7 @@ class Kaido : ZoroTheme( ) private val streamtapeExtractor by lazy { StreamTapeExtractor(client) } - private val megaCloudExtractor by lazy { MegaCloudExtractor(client, headers) } + private val megaCloudExtractor by lazy { MegaCloudExtractor(client, headers, preferences) } override fun extractVideo(server: VideoData): List