[Madara] Decrypt image list for Amuy and Cerise Scans (#14876)
* madata: add chapter image list decryption * add final newline * Exit early if there's no chapter protection Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * Change approach to individual sources multisrc doesn't like external dependencies * unbump baseversioncode Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
This commit is contained in:
parent
589f46de0b
commit
ca789eca79
|
@ -0,0 +1,3 @@
|
|||
dependencies {
|
||||
implementation(project(':lib-cryptoaes'))
|
||||
}
|
|
@ -1,8 +1,15 @@
|
|||
package eu.kanade.tachiyomi.extension.pt.amuy
|
||||
|
||||
import android.util.Base64
|
||||
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
|
||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.OkHttpClient
|
||||
import org.jsoup.nodes.Document
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -19,4 +26,44 @@ class Amuy : Madara(
|
|||
.build()
|
||||
|
||||
override val useNewChapterEndpoint = true
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val chapterProtector = document.getElementById("chapter-protector-data")?.html()
|
||||
?: return super.pageListParse(document)
|
||||
|
||||
val password = chapterProtector
|
||||
.substringAfter("wpmangaprotectornonce='")
|
||||
.substringBefore("';")
|
||||
val chapterData = json.parseToJsonElement(
|
||||
chapterProtector
|
||||
.substringAfter("chapter_data='")
|
||||
.substringBefore("';")
|
||||
.replace("\\/", "/")
|
||||
).jsonObject
|
||||
|
||||
val unsaltedCiphertext = Base64.decode(chapterData["ct"]!!.jsonPrimitive.content, Base64.DEFAULT)
|
||||
val salt = chapterData["s"]!!.jsonPrimitive.content.decodeHex()
|
||||
val ciphertext = SALTED + salt + unsaltedCiphertext
|
||||
|
||||
val rawImgArray = CryptoAES.decrypt(Base64.encodeToString(ciphertext, Base64.DEFAULT), password)
|
||||
val imgArrayString = json.parseToJsonElement(rawImgArray).jsonPrimitive.content
|
||||
val imgArray = json.parseToJsonElement(imgArrayString).jsonArray
|
||||
|
||||
return imgArray.mapIndexed { idx, it ->
|
||||
Page(idx, document.location(), it.jsonPrimitive.content)
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/66614516
|
||||
private fun String.decodeHex(): ByteArray {
|
||||
check(length % 2 == 0) { "Must have an even length" }
|
||||
|
||||
return chunked(2)
|
||||
.map { it.toInt(16).toByte() }
|
||||
.toByteArray()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val SALTED = "Salted__".toByteArray(Charsets.UTF_8)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
dependencies {
|
||||
implementation(project(':lib-cryptoaes'))
|
||||
}
|
|
@ -1,8 +1,15 @@
|
|||
package eu.kanade.tachiyomi.extension.pt.cerisescans
|
||||
|
||||
import android.util.Base64
|
||||
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
|
||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.OkHttpClient
|
||||
import org.jsoup.nodes.Document
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -19,4 +26,44 @@ class CeriseScans : Madara(
|
|||
.build()
|
||||
|
||||
override val useNewChapterEndpoint = true
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val chapterProtector = document.getElementById("chapter-protector-data")?.html()
|
||||
?: return super.pageListParse(document)
|
||||
|
||||
val password = chapterProtector
|
||||
.substringAfter("wpmangaprotectornonce='")
|
||||
.substringBefore("';")
|
||||
val chapterData = json.parseToJsonElement(
|
||||
chapterProtector
|
||||
.substringAfter("chapter_data='")
|
||||
.substringBefore("';")
|
||||
.replace("\\/", "/")
|
||||
).jsonObject
|
||||
|
||||
val unsaltedCiphertext = Base64.decode(chapterData["ct"]!!.jsonPrimitive.content, Base64.DEFAULT)
|
||||
val salt = chapterData["s"]!!.jsonPrimitive.content.decodeHex()
|
||||
val ciphertext = SALTED + salt + unsaltedCiphertext
|
||||
|
||||
val rawImgArray = CryptoAES.decrypt(Base64.encodeToString(ciphertext, Base64.DEFAULT), password)
|
||||
val imgArrayString = json.parseToJsonElement(rawImgArray).jsonPrimitive.content
|
||||
val imgArray = json.parseToJsonElement(imgArrayString).jsonArray
|
||||
|
||||
return imgArray.mapIndexed { idx, it ->
|
||||
Page(idx, document.location(), it.jsonPrimitive.content)
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/66614516
|
||||
private fun String.decodeHex(): ByteArray {
|
||||
check(length % 2 == 0) { "Must have an even length" }
|
||||
|
||||
return chunked(2)
|
||||
.map { it.toInt(16).toByte() }
|
||||
.toByteArray()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val SALTED = "Salted__".toByteArray(Charsets.UTF_8)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -358,6 +358,7 @@ abstract class Madara(
|
|||
if (list.isNotEmpty()) { list.forEach { genre -> url.addQueryParameter("genre[]", genre.id) } }
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
return GET(url.toString(), headers)
|
||||
|
@ -488,6 +489,7 @@ abstract class Madara(
|
|||
taxQueryIdx++
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -816,7 +818,7 @@ abstract class Madara(
|
|||
}
|
||||
open val updatingRegex = "Updating|Atualizando".toRegex(RegexOption.IGNORE_CASE)
|
||||
|
||||
public fun String.notUpdating(): Boolean {
|
||||
fun String.notUpdating(): Boolean {
|
||||
return this.contains(updatingRegex).not()
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class MadaraGenerator : ThemeSourceGenerator {
|
|||
SingleLang("Aleatória Scan", "https://aleatoriascan.xyz", "pt-BR", className = "AleatoriaScan"),
|
||||
SingleLang("AllPornComic", "https://allporncomic.com", "en", isNsfw = true),
|
||||
SingleLang("Aln Scans", "https://alnscans.com", "en"),
|
||||
SingleLang("Amuy", "https://amuyscans.com", "pt-BR", isNsfw = true),
|
||||
SingleLang("Amuy", "https://amuyscans.com", "pt-BR", isNsfw = true, overrideVersionCode = 1),
|
||||
SingleLang("Anikiga", "https://anikiga.com", "tr"),
|
||||
SingleLang("Anisa Manga", "https://anisamanga.com", "tr"),
|
||||
SingleLang("Ansh Scans", "https://anshscans.org", "en"),
|
||||
|
@ -64,7 +64,7 @@ class MadaraGenerator : ThemeSourceGenerator {
|
|||
SingleLang("CAT-translator", "https://cats-translator.com/manga", "th", className = "CatTranslator", overrideVersionCode = 2),
|
||||
SingleLang("Cat300", "https://cat300.com", "th", isNsfw = true, className = "Cat300", overrideVersionCode = 1),
|
||||
SingleLang("CatOnHeadTranslations", "https://catonhead.com", "en", overrideVersionCode = 2),
|
||||
SingleLang("Cerise Scans", "https://cerisescan.com", "pt-BR", overrideVersionCode = 3),
|
||||
SingleLang("Cerise Scans", "https://cerisescan.com", "pt-BR", overrideVersionCode = 4),
|
||||
SingleLang("Chibi Manga", "https://www.cmreader.info", "en", overrideVersionCode = 1),
|
||||
SingleLang("Clover Manga", "https://clover-manga.com", "tr", overrideVersionCode = 2),
|
||||
SingleLang("Coffee Manga", "https://coffeemanga.io", "en", overrideVersionCode = 1),
|
||||
|
|
Loading…
Reference in New Issue