LeviatanScans: fix page list (#16649)

* LevitanScans: fix page list

Co-authored-by: beerpsi <92439990+beerpiss@users.noreply.github.com>

* fix name

---------

Co-authored-by: beerpsi <92439990+beerpiss@users.noreply.github.com>
This commit is contained in:
AwkwardPeak7 2023-06-06 14:20:16 +05:00 committed by GitHub
parent 8b15072a4c
commit bdb8f96eed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 119 additions and 59 deletions

View File

@ -0,0 +1,3 @@
dependencies {
implementation(project(':lib-cryptoaes'))
}

View File

@ -0,0 +1,102 @@
package eu.kanade.tachiyomi.extension.all.leviatanscans
import android.util.Base64
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
abstract class LeviatanScans(
baseUrl: String,
lang: String,
dateFormat: SimpleDateFormat,
) : Madara(
"Leviatan Scans",
baseUrl,
lang,
dateFormat,
) {
override val useNewChapterEndpoint: Boolean = true
override fun chapterListSelector() = "li.wp-manga-chapter:not(.premium-block)"
override fun popularMangaFromElement(element: Element) =
replaceRandomUrlPartInManga(super.popularMangaFromElement(element))
override fun latestUpdatesFromElement(element: Element) =
replaceRandomUrlPartInManga(super.latestUpdatesFromElement(element))
override fun searchMangaFromElement(element: Element) =
replaceRandomUrlPartInManga(super.searchMangaFromElement(element))
override fun chapterFromElement(element: Element): SChapter {
val chapter = replaceRandomUrlPartInChapter(super.chapterFromElement(element))
with(element) {
selectFirst(chapterUrlSelector)?.let { urlElement ->
chapter.name = urlElement.ownText()
}
}
return chapter
}
private fun replaceRandomUrlPartInManga(manga: SManga): SManga {
val split = manga.url.split("/")
manga.url = split.slice(split.indexOf("manga") until split.size).joinToString("/", "/")
return manga
}
private fun replaceRandomUrlPartInChapter(chapter: SChapter): SChapter {
val split = chapter.url.split("/")
chapter.url = baseUrl + split.slice(split.indexOf("manga") until split.size).joinToString("/", "/")
return chapter
}
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)
}
}

View File

@ -1,13 +1,9 @@
package eu.kanade.tachiyomi.extension.all.leviatanscans
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.OkHttpClient
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
@ -18,64 +14,23 @@ class LeviatanScansFactory : SourceFactory {
)
}
class LeviatanScansEN : Madara("Leviatan Scans", "https://en.leviatanscans.com", "en", SimpleDateFormat("MMM dd, yyyy", Locale.US)) {
class LeviatanScansEN : LeviatanScans(
"https://en.leviatanscans.com",
"en",
SimpleDateFormat("MMM dd, yyyy", Locale.US),
) {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2)
.build()
override val useNewChapterEndpoint: Boolean = true
override fun popularMangaFromElement(element: Element) =
replaceRandomUrlPartInManga(super.popularMangaFromElement(element))
override fun latestUpdatesFromElement(element: Element) =
replaceRandomUrlPartInManga(super.latestUpdatesFromElement(element))
override fun searchMangaFromElement(element: Element) =
replaceRandomUrlPartInManga(super.searchMangaFromElement(element))
override fun chapterFromElement(element: Element): SChapter {
val chapter = replaceRandomUrlPartInChapter(super.chapterFromElement(element))
with(element) {
selectFirst(chapterUrlSelector)?.let { urlElement ->
chapter.name = urlElement.ownText()
}
}
return chapter
}
override val mangaDetailsSelectorDescription = "div.manga-summary"
override val mangaDetailsSelectorAuthor = "div.manga-authors"
}
class LeviatanScansES : LeviatanScans(
"https://es.leviatanscans.com",
"es",
SimpleDateFormat("MMM dd, yy", Locale("es")),
) {
override val mangaDetailsSelectorStatus = ".post-content_item:contains(Status) .summary-content"
}
class LeviatanScansES : Madara("Leviatan Scans", "https://es.leviatanscans.com", "es", SimpleDateFormat("MMM, yy", Locale("es"))) {
override val useNewChapterEndpoint: Boolean = true
override fun popularMangaFromElement(element: Element) =
replaceRandomUrlPartInManga(super.popularMangaFromElement(element))
override fun latestUpdatesFromElement(element: Element) =
replaceRandomUrlPartInManga(super.latestUpdatesFromElement(element))
override fun searchMangaFromElement(element: Element) =
replaceRandomUrlPartInManga(super.searchMangaFromElement(element))
override fun chapterFromElement(element: Element) =
replaceRandomUrlPartInChapter(super.chapterFromElement(element))
override val mangaDetailsSelectorStatus = ".post-content_item:contains(Status) .summary-content"
}
fun Madara.replaceRandomUrlPartInManga(manga: SManga): SManga {
val split = manga.url.split("/")
manga.url = split.slice(split.indexOf("manga") until split.size).joinToString("/", "/")
return manga
}
fun Madara.replaceRandomUrlPartInChapter(chapter: SChapter): SChapter {
val split = chapter.url.split("/")
chapter.url = baseUrl + split.slice(split.indexOf("manga") until split.size).joinToString("/", "/")
return chapter
}

View File

@ -14,7 +14,7 @@ class MadaraGenerator : ThemeSourceGenerator {
override val sources = listOf(
MultiLang("Atlantis Scan", "https://atlantisscan.com", listOf("es", "pt-BR"), isNsfw = true),
MultiLang("Leviatan Scans", "https://leviatanscans.com", listOf("en", "es"), className = "LeviatanScansFactory", overrideVersionCode = 13),
MultiLang("Leviatan Scans", "https://leviatanscans.com", listOf("en", "es"), className = "LeviatanScansFactory", overrideVersionCode = 14),
MultiLang("MangaForFree.net", "https://mangaforfree.net", listOf("en", "ko", "all"), isNsfw = true, className = "MangaForFreeFactory", pkgName = "mangaforfree", overrideVersionCode = 1),
MultiLang("Manhwa18.cc", "https://manhwa18.cc", listOf("en", "ko", "all"), isNsfw = true, className = "Manhwa18CcFactory", pkgName = "manhwa18cc", overrideVersionCode = 4),
MultiLang("Reaper Scans", "https://reaperscans.com", listOf("fr", "tr"), className = "ReaperScansFactory", pkgName = "reaperscans", overrideVersionCode = 12),