diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt index c1ba6782..521f5266 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt @@ -35,13 +35,30 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser( override val configKeyDomain = ConfigKey.Domain( "bato.to", - "mto.to", - "hto.to", - "mangatoto.com", - "battwo.com", + "batocomic.com", + "batocomic.net", + "batocomic.org", + "batotoo.com", "batotwo.com", + "battwo.com", "comiko.net", - "batotoo.com", + "comiko.org", + "mangatoto.com", + "mangatoto.net", + "mangatoto.org", + "readtoto.com", + "readtoto.net", + "readtoto.org", + "dto.to", + "hto.to", + "mto.to", + "wto.to", + "xbato.com", + "xbato.net", + "xbato.org", + "zbato.com", + "zbato.net", + "zbato.org", ) override suspend fun getListPage( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt index 031b7f0c..bb870482 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScan.kt @@ -29,7 +29,7 @@ import org.koitharu.kotatsu.parsers.util.urlEncoded import java.text.SimpleDateFormat import java.util.EnumSet -@MangaSourceParser("ISEKAISCAN", "Isekai Scan", "en") +@MangaSourceParser("ISEKAISCAN", "Isekai Scan Top", "en") internal class IsekaiScan(context: MangaLoaderContext) : MadaraParser(context, MangaSource.ISEKAISCAN, "isekaiscan.top", 16) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScanEuParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScanEuParser.kt index 0cbcf312..e9a48b26 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScanEuParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/en/IsekaiScanEuParser.kt @@ -21,7 +21,7 @@ import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl import org.koitharu.kotatsu.parsers.util.toTitleCase import org.koitharu.kotatsu.parsers.util.urlEncoded -@MangaSourceParser("ISEKAISCAN_EU", "IsekaiScan", "en") +@MangaSourceParser("ISEKAISCAN_EU", "Isekai Scan To", "en") internal class IsekaiScanEuParser(context: MangaLoaderContext) : MadaraParser(context, MangaSource.ISEKAISCAN_EU, "m.isekaiscan.to") { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ImmortalUpdatesId.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ImmortalUpdatesId.kt new file mode 100644 index 00000000..1602a973 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/id/ImmortalUpdatesId.kt @@ -0,0 +1,15 @@ +package org.koitharu.kotatsu.parsers.site.madara.id + +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("IMMORTALUPDATESID", "Immortal Updates Id", "id") +internal class ImmortalUpdatesId(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.IMMORTALUPDATESID, "immortalupdates.id") { + + override val datePattern = "d MMMM yyyy" + override val sourceLocale: Locale = Locale.ENGLISH +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt index ac8eb98c..0af080cf 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/MangaReaderParser.kt @@ -55,10 +55,11 @@ internal abstract class MangaReaderParser( private val mutex = Mutex() private var lastSearchPage = 1 + protected open val selectChapter = "#chapterlist > ul > li" override suspend fun getDetails(manga: Manga): Manga { val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val dateFormat = SimpleDateFormat(datePattern, sourceLocale) - val chapters = docs.select("#chapterlist > ul > li").mapChapters(reversed = true) { index, element -> + val chapters = docs.select(selectChapter).mapChapters(reversed = true) { index, element -> val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null MangaChapter( id = generateUid(url), @@ -99,7 +100,7 @@ internal abstract class MangaReaderParser( ?: tablemode.selectFirst(".infotable td:contains(حالة العمل)") ?: tablemode.selectFirst(".infotable td:contains(الحالة)") ?: tablemode.selectFirst(".infotable td:contains(Estado)") - ?: docs.selectFirst(".infotable td:contains(สถานะ)") + ?: tablemode.selectFirst(".infotable td:contains(สถานะ)") ?: tablemode.selectFirst(".infotable td:contains(Stato )") ?: tablemode.selectFirst(".infotable td:contains(Durum)") ?: tablemode.selectFirst(".infotable td:contains(Statüsü)") @@ -210,10 +211,11 @@ internal abstract class MangaReaderParser( return parseMangaList(webClient.httpGet(url).parseHtml()) } - protected open val selectMangaliste = ".postbody .listupd .bs .bsx" + protected open val selectMangalist = ".postbody .listupd .bs .bsx" + protected open val selectMangaListImg = "img.ts-post-image" protected open fun parseMangaList(docs: Document): List { - return docs.select(selectMangaliste).mapNotNull { + return docs.select(selectMangalist).mapNotNull { val a = it.selectFirst("a") ?: return@mapNotNull null val relativeUrl = a.attrAsRelativeUrl("href") val rating = it.selectFirst(".numscore")?.text() @@ -227,7 +229,7 @@ internal abstract class MangaReaderParser( publicUrl = a.attrAsAbsoluteUrl("href"), rating = rating, isNsfw = isNsfwSource, - coverUrl = it.selectFirst("img.ts-post-image")?.imageUrl().orEmpty(), + coverUrl = it.selectFirst(selectMangaListImg)?.imageUrl().orEmpty(), tags = emptySet(), state = null, author = null, @@ -239,13 +241,14 @@ internal abstract class MangaReaderParser( protected open val encodedSrc = false protected open val selectScript = "div.wrapper script" + protected open val selectPage = "div#readerarea img" override suspend fun getPages(chapter: MangaChapter): List { val chapterUrl = chapter.url.toAbsoluteUrl(domain) val docs = webClient.httpGet(chapterUrl).parseHtml() val test = docs.select("script:containsData(ts_reader)") if (test.isNullOrEmpty() and !encodedSrc) { - return docs.select("div#readerarea img").map { img -> + return docs.select(selectPage).map { img -> val url = img.imageUrl() MangaPage( id = generateUid(url), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt index 42ebe277..49c099e3 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/ar/SwaTeam.kt @@ -8,6 +8,7 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl import org.koitharu.kotatsu.parsers.util.domain @@ -18,13 +19,71 @@ import org.koitharu.kotatsu.parsers.util.parseHtml import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl import org.koitharu.kotatsu.parsers.util.toTitleCase import org.koitharu.kotatsu.parsers.util.tryParse +import org.koitharu.kotatsu.parsers.util.urlEncoded import java.text.SimpleDateFormat @MangaSourceParser("SWATEAM", "Swa Team", "ar") internal class SwaTeam(context: MangaLoaderContext) : MangaReaderParser(context, MangaSource.SWATEAM, "swatop.club", pageSize = 42, searchPageSize = 39) { - override val datePattern = "dd-MM-yyyy" + override val datePattern = "MMMM dd, yyyy" + override val selectMangalist = ".listupd .bs .bsx" + override val selectMangaListImg = "img" + + private var lastSearchPage = 1 + + // Tag doesn't work on manga page ( it comes from website ) + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + if (!query.isNullOrEmpty()) { + if (page > lastSearchPage) { + return emptyList() + } + + val url = buildString { + append("https://") + append(domain) + append("/?s=") + append(query.urlEncoded()) + append("&page=") + append(page) + } + + val docs = webClient.httpGet(url).parseHtml() + lastSearchPage = docs.selectFirst(".pagination .next") + ?.previousElementSibling() + ?.text()?.toIntOrNull() ?: 1 + return parseMangaList(docs) + } + + val sortQuery = when (sortOrder) { + SortOrder.ALPHABETICAL -> "title" + SortOrder.NEWEST -> "latest" + SortOrder.POPULARITY -> "popular" + SortOrder.UPDATED -> "update" + else -> "" + } + val tagKey = "genre[]".urlEncoded() + val tagQuery = + if (tags.isNullOrEmpty()) "" else tags.joinToString(separator = "&", prefix = "&") { "$tagKey=${it.key}" } + val url = buildString { + append("https://") + append(domain) + append(listUrl) + append("/?order=") + append(sortQuery) + append(tagQuery) + append("&page=") + append(page) + } + + return parseMangaList(webClient.httpGet(url).parseHtml()) + } + override suspend fun getDetails(manga: Manga): Manga { val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() @@ -37,7 +96,7 @@ internal class SwaTeam(context: MangaLoaderContext) : url = url, number = index + 1, scanlator = null, - uploadDate = dateFormat.tryParse(element.selectFirst(".chapterdate")?.text()), + uploadDate = dateFormat.tryParse(element.selectFirst(".chapter-date")?.text()), branch = null, source = source, ) @@ -52,22 +111,20 @@ internal class SwaTeam(context: MangaLoaderContext) : val states = docs.selectFirst("div.spe span:contains(Ongoing)")?.text() val state = if (states.isNullOrEmpty()) { - "Completed" + "completed" } else { - "Ongoing" + "ongoing" } val mangaState = state.let { when (it) { - "Ongoing" -> MangaState.ONGOING + "ongoing" -> MangaState.ONGOING - "Completed" -> MangaState.FINISHED + "completed" -> MangaState.FINISHED else -> null } } - - val author = docs.selectFirst("span.author i")?.text() val nsfw = docs.selectFirst(".restrictcontainer") != null diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/BabelToon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/BabelToon.kt index 2d8a32fa..8a433a3f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/BabelToon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/BabelToon.kt @@ -21,7 +21,7 @@ internal class BabelToon(context: MangaLoaderContext) : MangaReaderParser(context, MangaSource.BABELTOON, "babeltoon.com", pageSize = 20, searchPageSize = 10) { override val listUrl = "/series" - override val selectMangaliste = ".postbody .listupd .maindet .inmain" + override val selectMangalist = ".postbody .listupd .maindet .inmain" override suspend fun getDetails(manga: Manga): Manga { val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Zahard.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Zahard.kt new file mode 100644 index 00000000..bf429de4 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/en/Zahard.kt @@ -0,0 +1,70 @@ +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.Manga +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.urlEncoded +import java.util.EnumSet + +@MangaSourceParser("ZAHARD", "Zahard", "en") +internal class Zahard(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.ZAHARD, "zahard.xyz", pageSize = 20, searchPageSize = 30) { + + override val listUrl = "/library" + override val selectChapter = "#chapterlist > ul > a" + override val selectPage = "div#chapter_imgs img" + + + override val sortOrders: Set + get() = EnumSet.of(SortOrder.NEWEST) + private var lastSearchPage = 1 + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + if (!query.isNullOrEmpty()) { + if (page > lastSearchPage) { + return emptyList() + } + + val url = buildString { + append("https://") + append(domain) + append(listUrl) + append("?search=") + append(query.urlEncoded()) + append("&page=") + append(page) + } + + val docs = webClient.httpGet(url).parseHtml() + lastSearchPage = docs.selectFirst("a[rel=next]") + ?.previousElementSibling() + ?.text()?.toIntOrNull() ?: 1 + return parseMangaList(docs) + } + + val tagKey = "tag".urlEncoded() + val tagQuery = + if (tags.isNullOrEmpty()) "" else tags.joinToString(separator = "&", prefix = "&") { "$tagKey=${it.key}" } + val url = buildString { + append("https://") + append(domain) + append(listUrl) + append("?page=") + append(page) + append(tagQuery) + + } + + return parseMangaList(webClient.httpGet(url).parseHtml()) + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mangakyo.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mangakyo.kt new file mode 100644 index 00000000..96823859 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/Mangakyo.kt @@ -0,0 +1,15 @@ +package org.koitharu.kotatsu.parsers.site.mangareader.id + +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 +import java.util.Locale + +@MangaSourceParser("MANGAKYO", "Mangakyo", "id") +internal class Mangakyo(context: MangaLoaderContext) : + MangaReaderParser(context, MangaSource.MANGAKYO, "mangakyo.org", pageSize = 40, searchPageSize = 20) { + + override val listUrl = "/komik" + override val datePattern = "MMM d, yyyy" +}