feat(multisrc): Add ZoroTheme generator (#2347)

This commit is contained in:
Secozzi
2023-10-10 22:42:11 +00:00
committed by GitHub
parent 8b08d19606
commit a33aadae87
28 changed files with 680 additions and 540 deletions

View File

@ -0,0 +1,21 @@
plugins {
id("com.android.library")
kotlin("android")
id("kotlinx-serialization")
}
android {
compileSdk = AndroidConfig.compileSdk
namespace = "eu.kanade.tachiyomi.lib.megacloudextractor"
defaultConfig {
minSdk = AndroidConfig.minSdk
}
}
dependencies {
compileOnly(libs.bundles.common)
implementation(project(":lib-cryptoaes"))
implementation(project(":lib-playlist-utils"))
}
// BUMPS: 0

View File

@ -0,0 +1,118 @@
package eu.kanade.tachiyomi.lib.megacloudextractor
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.serialization.json.Json
import kotlinx.serialization.json.jsonPrimitive
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()
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
private val cacheControl = CacheControl.Builder().noStore().build()
private val noCacheClient = client.newBuilder()
.cache(null)
.build()
companion object {
private val SERVER_URL = arrayOf("https://megacloud.tv", "https://rapid-cloud.co")
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 fun cipherTextCleaner(data: String, type: String): Pair<String, String> {
// 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<List<List<Int>>>(it) }
val (password, ciphertext, _) = indexPairs.fold(Triple("", data, 0)) { previous, item ->
val start = item.first() + previous.third
val end = start + item.last()
val passSubstr = data.substring(start, end)
val passPart = previous.first + passSubstr
val cipherPart = previous.second.replace(passSubstr, "")
Triple(passPart, cipherPart, previous.third + item.last())
}
return Pair(ciphertext, password)
}
private fun tryDecrypting(ciphered: String, type: String, attempts: Int = 0): String {
if (attempts > 2) throw Exception("PLEASE NUKE ANIWATCH AND CLOUDFLARE")
val (ciphertext, password) = cipherTextCleaner(ciphered, type)
return CryptoAES.decrypt(ciphertext, password).ifEmpty {
tryDecrypting(ciphered, type, attempts + 1)
}
}
fun getVideosFromUrl(url: String, type: String, name: String): List<Video> {
val video = getVideoDto(url)
val masterUrl = video.sources.first().file
val subs2 = video.tracks
?.filter { it.kind == "captions" }
?.map { Track(it.file, it.label) }
?: emptyList()
return playlistUtils.extractFromHls(
masterUrl,
videoNameGen = { "$name - $it - $type" },
subtitleList = subs2,
referer = "https://${url.toHttpUrl().host}/"
)
}
private fun getVideoDto(url: String): VideoDto {
val type = if (url.startsWith("https://megacloud.tv")) 0 else 1
val keyType = SOURCES_KEY[type]
val id = url.substringAfter(SOURCES_SPLITTER[type], "")
.substringBefore("?", "").ifEmpty { throw Exception("I HATE THE ANTICHRIST") }
val srcRes = client.newCall(GET(SERVER_URL[type] + SOURCES_URL[type] + id))
.execute()
.use { it.body.string() }
val data = json.decodeFromString<SourceResponseDto>(srcRes)
if (!data.encrypted) return json.decodeFromString<VideoDto>(srcRes)
val ciphered = data.sources.jsonPrimitive.content.toString()
val decrypted = json.decodeFromString<List<VideoLink>>(tryDecrypting(ciphered, keyType))
return VideoDto(decrypted, data.tracks)
}
@Serializable
data class VideoDto(
val sources: List<VideoLink>,
val tracks: List<TrackDto>? = null,
)
@Serializable
data class SourceResponseDto(
val sources: JsonElement,
val encrypted: Boolean = true,
val tracks: List<TrackDto>? = null,
)
@Serializable
data class VideoLink(val file: String = "")
@Serializable
data class TrackDto(val file: String, val kind: String, val label: String = "")
}