fix(lib/chillx-extractor): update key & small refactor (#1888)

This commit is contained in:
Secozzi 2023-07-11 12:28:16 +02:00 committed by GitHub
parent 554b06ba6e
commit 1f8de21098
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 38 deletions

View File

@ -1,6 +1,7 @@
plugins { plugins {
id("com.android.library") id("com.android.library")
kotlin("android") kotlin("android")
id("kotlinx-serialization")
} }
android { android {

View File

@ -683,7 +683,7 @@ import android.util.Base64
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
@ -707,37 +707,16 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
).execute().asJsoup().html() ).execute().asJsoup().html()
val master = Regex("""MasterJS\s*=\s*'([^']+)""").find(document)?.groupValues?.get(1) val master = Regex("""MasterJS\s*=\s*'([^']+)""").find(document)?.groupValues?.get(1)
val aesJson = Json.decodeFromString<JsonObject>(base64Decode(master ?: return null).toString(Charsets.UTF_8)) val aesJson = Json.decodeFromString<CryptoInfo>(base64Decode(master ?: return null).toString(Charsets.UTF_8))
val encData = val decrypt = cryptoAESHandler(aesJson ?: return null, KEY)
try {
AESData(
aesJson["ciphertext"]!!.jsonPrimitive.content,
aesJson["iv"]!!.jsonPrimitive.content,
aesJson["salt"]!!.jsonPrimitive.content,
aesJson["iterations"]!!.jsonPrimitive.content,
)
} catch(_: Exception) {
null
}
val decrypt = cryptoAESHandler(encData ?: return null, KEY)
val playlistUrl = Regex("""sources:\s*\[\{"file":"([^"]+)""").find(decrypt)?.groupValues?.get(1) ?: return null val playlistUrl = Regex("""sources:\s*\[\{"file":"([^"]+)""").find(decrypt)?.groupValues?.get(1) ?: return null
val tracks = Regex("""tracks:\s*\[(.+)]""").find(decrypt)?.groupValues?.get(1) val tracks = Regex("""tracks:\s*\[(.+)]""").find(decrypt)?.groupValues?.get(1)
val trackJson = Json.decodeFromString<JsonObject>(tracks ?: return null)
// TODO: Add subtitle support when a site is found that uses it
val trackData =
try {
SubtitleTrack( // TODO: Add subtitle support when a site is found that uses it
trackJson["file"]!!.jsonPrimitive.content, val trackJson = Json.decodeFromString<SubtitleTrack>(tracks ?: return null)
trackJson["label"]!!.jsonPrimitive.content,
trackJson["kind"]!!.jsonPrimitive.content,
)
} catch(_: Exception) {
null
}
val masterHeaders = Headers.headersOf( val masterHeaders = Headers.headersOf(
"Accept", "*/*", "Accept", "*/*",
@ -752,8 +731,8 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
val response = client.newCall(GET(playlistUrl, headers = masterHeaders)).execute() val response = client.newCall(GET(playlistUrl, headers = masterHeaders)).execute()
val masterPlaylist = response.body.string() val masterPlaylist = response.body.string()
masterPlaylist?.substringAfter("#EXT-X-STREAM-INF:") masterPlaylist.substringAfter("#EXT-X-STREAM-INF:")
?.split("#EXT-X-STREAM-INF:")?.map { .split("#EXT-X-STREAM-INF:").map {
val quality = it.substringAfter("RESOLUTION=").split(",")[0].split("\n")[0].substringAfter("x") + "p" val quality = it.substringAfter("RESOLUTION=").split(",")[0].split("\n")[0].substringAfter("x") + "p"
var videoUrl = it.substringAfter("\n").substringBefore("\n") var videoUrl = it.substringAfter("\n").substringBefore("\n")
@ -771,14 +750,14 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
} }
private fun cryptoAESHandler( private fun cryptoAESHandler(
data: AESData, data: CryptoInfo,
pass: String, pass: String,
): String { ): String {
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512") val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512")
val spec = PBEKeySpec( val spec = PBEKeySpec(
pass.toCharArray(), pass.toCharArray(),
data.salt?.hexToByteArray(), data.salt?.hexToByteArray(),
data.iterations?.toIntOrNull() ?: 1, data.iterations ?: 1,
256 256
) )
val key = factory.generateSecret(spec) val key = factory.generateSecret(spec)
@ -795,6 +774,14 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
return Base64.decode(string, Base64.DEFAULT) return Base64.decode(string, Base64.DEFAULT)
} }
@Serializable
data class CryptoInfo(
val ciphertext: String? = null,
val iv: String? = null,
val salt: String? = null,
val iterations: Int? = null,
)
private fun String.hexToByteArray(): ByteArray { private fun String.hexToByteArray(): ByteArray {
check(length % 2 == 0) { "Must have an even length" } check(length % 2 == 0) { "Must have an even length" }
return chunked(2) return chunked(2)
@ -803,13 +790,7 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
.toByteArray() .toByteArray()
} }
data class AESData( @Serializable
val ciphertext: String? = null,
val iv: String? = null,
val salt: String? = null,
val iterations: String? = null,
)
data class SubtitleTrack( data class SubtitleTrack(
val file: String? = null, val file: String? = null,
val label: String? = null, val label: String? = null,
@ -817,6 +798,6 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
) )
companion object { companion object {
private const val KEY = "4VqE3#N7zt&HEP^a" private const val KEY = "11x&W5UBrcqn\$9Yl"
} }
} }