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 cfa01bf12..26c9c7331 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 @@ -4,16 +4,20 @@ import eu.kanade.tachiyomi.animesource.model.Track import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils -import okhttp3.Headers import eu.kanade.tachiyomi.network.GET +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.jsonPrimitive +import okhttp3.CacheControl +import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import uy.kohesive.injekt.injectLazy -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonElement -import okhttp3.CacheControl -import okhttp3.HttpUrl.Companion.toHttpUrl class MegaCloudExtractor(private val client: OkHttpClient, private val headers: Headers) { private val json: Json by injectLazy() @@ -30,16 +34,26 @@ class MegaCloudExtractor(private val client: OkHttpClient, private val headers: private val SOURCES_URL = arrayOf("/embed-2/ajax/e-1/getSources?id=", "/ajax/embed-6-v2/getSources?id=") private val SOURCES_SPLITTER = arrayOf("/e-1/", "/embed-6-v2/") private val SOURCES_KEY = arrayOf("1", "6") + private val INDEX_PAIRS_MAP = mutableMapOf("1" to emptyList>(), "6" to emptyList>()) + private val MUTEX = Mutex() + + 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 { + noCacheClient.newCall(GET("https://raw.githubusercontent.com/Claudemirovsky/keys/e$type/key", cache = cacheControl)) + .execute() + .use { it.body.string() } + .let { json.decodeFromString>>(it) } + .also { INDEX_PAIRS_MAP[type] = it } + } } private fun cipherTextCleaner(data: String, type: String): Pair { - // TODO: fetch the key only when needed, using a thread-safe map - // (Like ConcurrentMap?) or MUTEX hacks. - val indexPairs = noCacheClient.newCall(GET("https://raw.githubusercontent.com/Claudemirovsky/keys/e$type/key", cache = cacheControl)) - .execute() - .use { it.body.string() } - .let { json.decodeFromString>>(it) } - + val indexPairs = getIndexPairs(type) val (password, ciphertext, _) = indexPairs.fold(Triple("", data, 0)) { previous, item -> val start = item.first() + previous.third val end = start + item.last() @@ -56,6 +70,8 @@ class MegaCloudExtractor(private val client: OkHttpClient, private val headers: if (attempts > 2) throw Exception("PLEASE NUKE ANIWATCH AND CLOUDFLARE") val (ciphertext, password) = cipherTextCleaner(ciphered, type) return CryptoAES.decrypt(ciphertext, password).ifEmpty { + // Update index pairs + runLocked { INDEX_PAIRS_MAP[type] = emptyList>() } tryDecrypting(ciphered, type, attempts + 1) } } @@ -67,7 +83,7 @@ class MegaCloudExtractor(private val client: OkHttpClient, private val headers: val subs2 = video.tracks ?.filter { it.kind == "captions" } ?.map { Track(it.file, it.label) } - ?: emptyList() + .orEmpty() return playlistUtils.extractFromHls( masterUrl, videoNameGen = { "$name - $it - $type" },