feat(lib/megacloud-extractor): Address TODO, fetch indexes only when needed (#2487)

This commit is contained in:
Claudemirovsky 2023-11-09 08:06:00 -03:00 committed by GitHub
parent 334aa751d3
commit b693107302
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -4,16 +4,20 @@ import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import okhttp3.Headers
import eu.kanade.tachiyomi.network.GET 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.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import okhttp3.CacheControl
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy 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) { class MegaCloudExtractor(private val client: OkHttpClient, private val headers: Headers) {
private val json: Json by injectLazy() 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_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_SPLITTER = arrayOf("/e-1/", "/embed-6-v2/")
private val SOURCES_KEY = arrayOf("1", "6") private val SOURCES_KEY = arrayOf("1", "6")
private val INDEX_PAIRS_MAP = mutableMapOf("1" to emptyList<List<Int>>(), "6" to emptyList<List<Int>>())
private val MUTEX = Mutex()
private inline fun <reified R> 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<List<List<Int>>>(it) }
.also { INDEX_PAIRS_MAP[type] = it }
}
} }
private fun cipherTextCleaner(data: String, type: String): Pair<String, String> { private fun cipherTextCleaner(data: String, type: String): Pair<String, String> {
// TODO: fetch the key only when needed, using a thread-safe map val indexPairs = getIndexPairs(type)
// (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<List<List<Int>>>(it) }
val (password, ciphertext, _) = indexPairs.fold(Triple("", data, 0)) { previous, item -> val (password, ciphertext, _) = indexPairs.fold(Triple("", data, 0)) { previous, item ->
val start = item.first() + previous.third val start = item.first() + previous.third
val end = start + item.last() 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") if (attempts > 2) throw Exception("PLEASE NUKE ANIWATCH AND CLOUDFLARE")
val (ciphertext, password) = cipherTextCleaner(ciphered, type) val (ciphertext, password) = cipherTextCleaner(ciphered, type)
return CryptoAES.decrypt(ciphertext, password).ifEmpty { return CryptoAES.decrypt(ciphertext, password).ifEmpty {
// Update index pairs
runLocked { INDEX_PAIRS_MAP[type] = emptyList<List<Int>>() }
tryDecrypting(ciphered, type, attempts + 1) tryDecrypting(ciphered, type, attempts + 1)
} }
} }
@ -67,7 +83,7 @@ class MegaCloudExtractor(private val client: OkHttpClient, private val headers:
val subs2 = video.tracks val subs2 = video.tracks
?.filter { it.kind == "captions" } ?.filter { it.kind == "captions" }
?.map { Track(it.file, it.label) } ?.map { Track(it.file, it.label) }
?: emptyList() .orEmpty()
return playlistUtils.extractFromHls( return playlistUtils.extractFromHls(
masterUrl, masterUrl,
videoNameGen = { "$name - $it - $type" }, videoNameGen = { "$name - $it - $type" },