Add, fix sources

Add language on Bato.To
pull/404/head
devi 2 years ago
parent b8caf8e572
commit a1a17c02d7

@ -96,7 +96,11 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser(
}, },
) )
} }
// langs= en ...
filter.locale?.let {
append("&langs=")
append(it.language)
}
if (filter.tags.isNotEmpty()) { if (filter.tags.isNotEmpty()) {
append("&genres=") append("&genres=")
@ -211,6 +215,19 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser(
throw ParseException("Cannot find gernes list", scripts[0].baseUri()) throw ParseException("Cannot find gernes list", scripts[0].baseUri())
} }
override suspend fun getAvailableLocales(): Set<Locale> {
val jsRaw = webClient.httpGet("https://$domain/amsta/build/jss-btoto_v22.js").parseRaw()
val langRaw = jsRaw.substringAfter("items: {").substringBefore(",\"_t\"").split("code\":\"").drop(1)
return langRaw.mapNotNullToSet {
val lang = it.substringBefore("\",\"")
if (lang.contains("-")) {
return@mapNotNullToSet null
} else {
Locale(lang)
}
}
}
private suspend fun search(page: Int, query: String): List<Manga> { private suspend fun search(page: Int, query: String): List<Manga> {
val url = buildString { val url = buildString {
append("https://") append("https://")

@ -65,38 +65,45 @@ internal class ExHentaiParser(
paginator.firstPage = 0 paginator.firstPage = 0
} }
override suspend fun getListPage( override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> {
page: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
var search = query?.urlEncoded().orEmpty()
val next = nextPages.get(page, 0L) val next = nextPages.get(page, 0L)
if (page > 0 && next == 0L) { if (page > 0 && next == 0L) {
assert(false) { "Page timestamp not found" } assert(false) { "Page timestamp not found" }
return emptyList() return emptyList()
} }
var search = ""
val url = buildString { val url = buildString {
append("https://") append("https://")
append(domain) append(domain)
append("/?next=") append("/?next=")
append(next) append(next)
if (!tags.isNullOrEmpty()) { when (filter) {
var fCats = 0
for (tag in tags) { is MangaListFilter.Search -> {
tag.key.toIntOrNull()?.let { fCats = fCats or it } ?: run { search += filter.query.urlEncoded()
search += tag.key + " " append("&f_search=")
} append(search.trim().replace(' ', '+'))
} }
if (fCats != 0) {
append("&f_cats=") is MangaListFilter.Advanced -> {
append(1023 - fCats) if (filter.tags.isNotEmpty()) {
var fCats = 0
for (tag in filter.tags) {
tag.key.toIntOrNull()?.let { fCats = fCats or it } ?: run {
search += tag.key + " "
}
}
if (fCats != 0) {
append("&f_cats=")
append(1023 - fCats)
}
}
} }
}
if (search.isNotEmpty()) { null -> {}
append("&f_search=")
append(search.trim().replace(' ', '+'))
} }
// by unknown reason cookie "sl=dm_2" is ignored, so, we should request it again // by unknown reason cookie "sl=dm_2" is ignored, so, we should request it again
if (updateDm) { if (updateDm) {
@ -107,6 +114,7 @@ internal class ExHentaiParser(
append("&f_sh=on") append("&f_sh=on")
} }
} }
val body = webClient.httpGet(url).parseHtml().body() val body = webClient.httpGet(url).parseHtml().body()
val root = body.selectFirst("table.itg") val root = body.selectFirst("table.itg")
?.selectFirst("tbody") ?.selectFirst("tbody")
@ -114,7 +122,7 @@ internal class ExHentaiParser(
body.parseFailed("Cannot find root") body.parseFailed("Cannot find root")
} else { } else {
updateDm = true updateDm = true
return getListPage(page, query, tags, sortOrder) return getListPage(page, filter)
} }
updateDm = false updateDm = false
nextPages[page + 1] = getNextTimestamp(body) nextPages[page + 1] = getNextTimestamp(body)

@ -158,8 +158,7 @@ internal abstract class MadaraParser(
protected open val listUrl = "manga/" protected open val listUrl = "manga/"
override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> { override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> {
if (withoutAjax) {
val doc = if (withoutAjax) {
val pages = page + 1 val pages = page + 1
val url = buildString { val url = buildString {
@ -224,7 +223,7 @@ internal abstract class MadaraParser(
} }
} }
} }
webClient.httpGet(url).parseHtml() return parseMangaList(webClient.httpGet(url).parseHtml())
} else { } else {
val payload = if (filter?.sortOrder == SortOrder.RATING) { val payload = if (filter?.sortOrder == SortOrder.RATING) {
createRequestTemplate(ratingRequest) createRequestTemplate(ratingRequest)
@ -274,12 +273,16 @@ internal abstract class MadaraParser(
} }
} }
webClient.httpPost( return parseMangaList(
"https://$domain/wp-admin/admin-ajax.php", webClient.httpPost(
payload, "https://$domain/wp-admin/admin-ajax.php",
).parseHtml() payload,
).parseHtml(),
)
} }
}
protected open fun parseMangaList(doc: Document): List<Manga> {
return doc.select("div.row.c-tabs-item__content").ifEmpty { return doc.select("div.row.c-tabs-item__content").ifEmpty {
doc.select("div.page-item-detail") doc.select("div.page-item-detail")
}.map { div -> }.map { div ->

@ -1,13 +1,52 @@
package org.koitharu.kotatsu.parsers.site.madara.ar package org.koitharu.kotatsu.parsers.site.madara.ar
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrlOrNull
import org.koitharu.kotatsu.parsers.util.domain
import org.koitharu.kotatsu.parsers.util.generateUid
import org.koitharu.kotatsu.parsers.util.mapChapters
import org.koitharu.kotatsu.parsers.util.parseFailed
import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.removeSuffix
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
import java.text.SimpleDateFormat
@MangaSourceParser("MANGA_LEK", "Manga-Lek", "ar") @MangaSourceParser("MANGA_LEK", "Manga-Lek", "ar")
internal class MangaLekCom(context: MangaLoaderContext) : internal class MangaLekCom(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.MANGA_LEK, "manga-lek.com") { MadaraParser(context, MangaSource.MANGA_LEK, "manga-lek.com") {
override val listUrl = "mangalek/" override val listUrl = "mangalek/"
override val postReq = true override val postReq = true
override suspend fun loadChapters(mangaUrl: String, document: Document): List<MangaChapter> {
val mangaId = document.select("div#manga-chapters-holder").attr("data-id")
val url = "https://$domain/wp-admin/admin-ajax.php"
val postData = "action=manga_get_chapters&manga=$mangaId"
val doc = webClient.httpPost(url, postData).parseHtml()
val dateFormat = SimpleDateFormat(datePattern, sourceLocale)
return doc.select(selectChapter).mapChapters { i, li ->
val a = li.selectFirst("a")
val href = a?.attrAsRelativeUrlOrNull("href") ?: li.parseFailed("Link is missing")
val link = href + stylePage
val dateText = li.selectFirst("a.c-new-tag")?.attr("title") ?: li.selectFirst(selectDate)?.text()
val name = a.selectFirst("p")?.text() ?: a.ownText()
MangaChapter(
id = generateUid(href),
url = link,
name = name,
number = i + 1,
branch = null,
uploadDate = parseChapterDate(
dateFormat,
dateText,
),
scanlator = null,
source = source,
)
}
}
} }

@ -5,6 +5,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("MANGALIKE", "MangaLike", "ar") @MangaSourceParser("MANGALIKE", "MangaLike.net", "ar")
internal class MangaLike(context: MangaLoaderContext) : internal class MangaLike(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.MANGALIKE, "manga-like.net", pageSize = 10) MadaraParser(context, MangaSource.MANGALIKE, "manga-like.net", pageSize = 10)

@ -0,0 +1,84 @@
package org.koitharu.kotatsu.parsers.site.madara.en
import org.json.JSONObject
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.*
@MangaSourceParser("FIRESCANS", "FireScans", "en")
internal class FireScans(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.FIRESCANS, "firescans.xyz", 10) {
override fun parseMangaList(doc: Document): List<Manga> {
return doc.select("div.row.c-tabs-item__content").ifEmpty {
doc.select("div.page-item-detail")
}.map { div ->
val href = div.selectFirst("h3 a")?.attrAsRelativeUrlOrNull("href") ?: div.parseFailed("Link not found")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
Manga(
id = generateUid(href),
url = href,
publicUrl = href.toAbsoluteUrl(div.host ?: domain),
coverUrl = div.selectFirst("img")?.src().orEmpty(),
title = (summary?.selectFirst("h3") ?: summary?.selectFirst("h4")
?: div.selectFirst(".manga-name"))?.text().orEmpty(),
altTitle = null,
rating = div.selectFirst("span.total_votes")?.ownText()?.toFloatOrNull()?.div(5f) ?: -1f,
tags = summary?.selectFirst(".mg_genres")?.select("a")?.mapNotNullToSet { a ->
MangaTag(
key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
title = a.text().ifEmpty { return@mapNotNullToSet null }.toTitleCase(),
source = source,
)
}.orEmpty(),
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()) {
in ongoing -> MangaState.ONGOING
in finished -> MangaState.FINISHED
in abandoned -> MangaState.ABANDONED
in paused -> MangaState.PAUSED
else -> null
},
source = source,
isNsfw = isNsfwSource,
)
}
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val chapterProtector = doc.requireElementById("chapter-protector-data")
val chapterProtectorHtml =
context.decodeBase64(chapterProtector.attr("src").removePrefix("data:text/javascript;base64,"))
.toString(Charsets.UTF_8)
val password = chapterProtectorHtml.substringAfter("wpmangaprotectornonce='").substringBefore("';")
val chapterData = JSONObject(
chapterProtectorHtml.substringAfter("chapter_data='").substringBefore("';").replace("\\/", "/"),
)
val unsaltedCiphertext = context.decodeBase64(chapterData.getString("ct"))
val salt = chapterData.getString("s").toString().decodeHex()
val ciphertext = "Salted__".toByteArray(Charsets.UTF_8) + salt + unsaltedCiphertext
val rawImgArray = CryptoAES(context).decrypt(context.encodeBase64(ciphertext), password)
val imgArrayString = rawImgArray.filterNot { c -> c == '[' || c == ']' || c == '\\' || c == '"' }
return imgArrayString.split(",").map { url ->
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source,
)
}
}
fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2).map { it.toInt(16).toByte() }.toByteArray()
}
}

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.madara.es
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("HOUSEMANGAS", "HouseMangas", "es")
internal class HouseMangas(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.HOUSEMANGAS, "housemangas.com")

@ -0,0 +1,12 @@
package org.koitharu.kotatsu.parsers.site.madara.es
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("LECTORUNM", "Lectorunm.life", "es")
internal class Lectorunm(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.LECTORUNM, "lectorunm.life") {
override val datePattern = "dd/MM/yyyy"
}

@ -5,8 +5,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("NOBLESSETRANSLATIONS", "Noblesse Translations", "es") @MangaSourceParser("NOBLESSETRANSLATIONS", "NoblesseTranslations", "es")
internal class NoblesseTranslations(context: MangaLoaderContext) : internal class NoblesseTranslations(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.NOBLESSETRANSLATIONS, "noblessetranslations.com") { MadaraParser(context, MangaSource.NOBLESSETRANSLATIONS, "www.noblessev1.com")
override val datePattern = "d MMMM, yyyy"
}

@ -0,0 +1,14 @@
package org.koitharu.kotatsu.parsers.site.madara.es
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import java.util.Locale
@MangaSourceParser("RICHTOSCAN", "RichtoScan", "es")
internal class RichtoScan(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.RICHTOSCAN, "richtoscan.com") {
override val tagPrefix = "manga-generos/"
override val sourceLocale: Locale = Locale.ENGLISH
}

@ -2,9 +2,44 @@ package org.koitharu.kotatsu.parsers.site.madara.fr
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.*
@MangaSourceParser("FRSCAN", "Fr-Scan", "fr") @MangaSourceParser("FRSCAN", "Fr-Scan", "fr")
internal class FrScan(context: MangaLoaderContext) : internal class FrScan(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.FRSCAN, "fr-scan.com") MadaraParser(context, MangaSource.FRSCAN, "fr-scan.com") {
override val stylePage = ""
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val preloadImg = doc.getElementById("chapter_preloaded_images")
if (preloadImg != null) {
val img = preloadImg.data().substringAfter("[").substringBeforeLast("]")
.replace("\\", "").replace("\"", "").split(",")
return img.map { url ->
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source,
)
}
} else {
return doc.body().selectFirstOrThrow(selectBodyPage).select(selectPage).map { div ->
val img = div.selectFirstOrThrow("img")
val url = img.src()?.toRelativeUrl(domain) ?: div.parseFailed("Image src not found")
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source,
)
}
}
}
}

@ -0,0 +1,11 @@
package org.koitharu.kotatsu.parsers.site.madara.pt
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("CVNSCAN", "CvnScan", "pt", ContentType.HENTAI)
internal class CvnScan(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.CVNSCAN, "cvnscan.com")

@ -0,0 +1,42 @@
package org.koitharu.kotatsu.parsers.site.madara.vi
import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrlOrNull
import org.koitharu.kotatsu.parsers.util.domain
import org.koitharu.kotatsu.parsers.util.generateUid
import org.koitharu.kotatsu.parsers.util.mapChapters
import org.koitharu.kotatsu.parsers.util.parseFailed
import org.koitharu.kotatsu.parsers.util.parseHtml
@MangaSourceParser("TRUYENTRANHDAMMYY", "TruyenTranhDamMyy", "vi")
internal class TruyenTranhDamMyy(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.TRUYENTRANHDAMMYY, "truyentranhdammyy.com") {
override val postReq = true
override suspend fun loadChapters(mangaUrl: String, document: Document): List<MangaChapter> {
val mangaId = document.select("div#manga-chapters-holder").attr("data-id")
val url = "https://$domain/wp-admin/admin-ajax.php"
val postData = "action=manga_get_chapters&manga=$mangaId"
val doc = webClient.httpPost(url, postData).parseHtml()
return doc.select(selectChapter).mapChapters(reversed = true) { i, li ->
val a = li.selectFirst("a")
val href = a?.attrAsRelativeUrlOrNull("href") ?: li.parseFailed("Link is missing")
val link = href + stylePage
val name = a.selectFirst("p")?.text() ?: a.ownText()
MangaChapter(
id = generateUid(href),
url = link,
name = name,
number = i + 1,
branch = null,
uploadDate = 0, // Correct datePattern not found.
scanlator = null,
source = source,
)
}
}
}

@ -0,0 +1,12 @@
package org.koitharu.kotatsu.parsers.site.mangareader.en
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
@MangaSourceParser("AGSCOMICS", "AgsComics", "en")
internal class AgsComics(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.AGSCOMICS, "agscomics.com", pageSize = 20, searchPageSize = 10) {
override val listUrl = "/series"
}

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.site.mangareader.en
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
@MangaSourceParser("ASCALONSCANS", "AscalonScans", "en")
internal class AscalonScans(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.ASCALONSCANS, "ascalonscans.com", pageSize = 20, searchPageSize = 10)

@ -7,6 +7,6 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
@MangaSourceParser("KAISCANS", "KaiScans", "en") @MangaSourceParser("KAISCANS", "KaiScans", "en")
internal class KaiScans(context: MangaLoaderContext) : internal class KaiScans(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.KAISCANS, "kaiscans.com", pageSize = 20, searchPageSize = 10) { MangaReaderParser(context, MangaSource.KAISCANS, "www.kaiscans.com", pageSize = 20, searchPageSize = 10) {
override val listUrl = "/series" override val listUrl = "/series"
} }

@ -8,6 +8,5 @@ import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
@MangaSourceParser("RAVENSCANS", "RavenScans", "en") @MangaSourceParser("RAVENSCANS", "RavenScans", "en")
internal class RavenScans(context: MangaLoaderContext) : internal class RavenScans(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.RAVENSCANS, "ravenscans.com", pageSize = 10, searchPageSize = 10) { MangaReaderParser(context, MangaSource.RAVENSCANS, "ravenscans.com", pageSize = 10, searchPageSize = 10) {
override val datePattern = "MMM d, yyyy" override val datePattern = "MMM d, yyyy"
} }

@ -0,0 +1,12 @@
package org.koitharu.kotatsu.parsers.site.mangareader.en
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
@MangaSourceParser("READERSPOINT", "ReadersPoint", "en")
internal class ReadersPoint(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.READERSPOINT, "readers-point.space", pageSize = 20, searchPageSize = 10) {
override val listUrl = "/series"
}
Loading…
Cancel
Save