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 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.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale 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() override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2) .rateLimit(1, 2)
.build() .build()
override val useNewChapterEndpoint: Boolean = true override val mangaDetailsSelectorDescription = "div.manga-summary"
override val mangaDetailsSelectorAuthor = "div.manga-authors"
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
} }
class LeviatanScansES : LeviatanScans(
"https://es.leviatanscans.com",
"es",
SimpleDateFormat("MMM dd, yy", Locale("es")),
) {
override val mangaDetailsSelectorStatus = ".post-content_item:contains(Status) .summary-content" 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( override val sources = listOf(
MultiLang("Atlantis Scan", "https://atlantisscan.com", listOf("es", "pt-BR"), isNsfw = true), 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("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("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), MultiLang("Reaper Scans", "https://reaperscans.com", listOf("fr", "tr"), className = "ReaperScansFactory", pkgName = "reaperscans", overrideVersionCode = 12),