diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt index 7706b51f..21b53487 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/GalleryAdultsParser.kt @@ -149,6 +149,9 @@ internal abstract class GalleryAdultsParser( val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val urlChapters = doc.selectFirstOrThrow(selectUrlChapter).attr("href") val tag = doc.selectFirst(selectTag)?.parseTags() + val branch = doc.select(selectLanguageChapter).joinToString(separator = " / ") { + it.html().substringBefore("<") + } return manga.copy( tags = tag.orEmpty(), author = doc.selectFirst(selectAuthor)?.html()?.substringBefore(" = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) + override suspend fun getListPage( page: Int, query: String?, tags: Set?, sortOrder: SortOrder, ): List { - val tag = tags.oneOrThrowIfMany() + if (query.isNullOrEmpty() && tags != null && tags.size > 1) { + return getListPage(page, buildQuery(tags), emptySet(), sortOrder) + } val url = buildString { append("https://") append(domain) if (!tags.isNullOrEmpty()) { - if (tag?.key == "languageKey") { + val tag = tags.single() + if (tag.key == "languageKey") { append("/language") append(tag.title) - append("/") - append(page) } else { append("/tags/") - append(tag?.key.orEmpty()) - append("/") - append(page) + append(tag.key) + } + append("/") + append(page) + if (sortOrder == SortOrder.POPULARITY) { + append("?sort=popular") } } else if (!query.isNullOrEmpty()) { - append("/search/?q=") + append("/search?q=") append(query.urlEncoded()) + if (sortOrder == SortOrder.POPULARITY) { + append("&sort=popular") + } append("&page=") append(page) } else { @@ -69,4 +79,13 @@ internal class Hentai3(context: MangaLoaderContext) : val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml() return doc.selectFirstOrThrow(idImg).src() ?: doc.parseFailed("Image src not found") } + + private fun buildQuery(tags: Collection) = + tags.joinToString(separator = " ") { tag -> + if (tag.key == "languageKey") { + "language:\"${tag.title.removePrefix("/")}\"" + } else { + "tag:\"${tag.title}\"" + } + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEnvy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEnvy.kt index 54e3e4b5..cb8a199a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEnvy.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEnvy.kt @@ -8,6 +8,7 @@ import org.koitharu.kotatsu.parsers.util.domain import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany import org.koitharu.kotatsu.parsers.util.parseHtml import org.koitharu.kotatsu.parsers.util.urlEncoded +import java.util.EnumSet @MangaSourceParser("HENTAIENVY", "HentaiEnvy", type = ContentType.HENTAI) internal class HentaiEnvy(context: MangaLoaderContext) : @@ -31,6 +32,8 @@ internal class HentaiEnvy(context: MangaLoaderContext) : "/portuguese", ) + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) + override suspend fun getListPage( page: Int, query: String?, @@ -49,6 +52,9 @@ internal class HentaiEnvy(context: MangaLoaderContext) : } else { append("/tag/") append(tag?.key.orEmpty()) + if (sortOrder == SortOrder.POPULARITY) { + append("/popular") + } append("/?") } } else if (!query.isNullOrEmpty()) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt index d84f1cb5..8d4628eb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiEra.kt @@ -6,6 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser import org.koitharu.kotatsu.parsers.util.* +import java.util.EnumSet @MangaSourceParser("HENTAIERA", "HentaiEra", type = ContentType.HENTAI) internal class HentaiEra(context: MangaLoaderContext) : @@ -24,6 +25,8 @@ internal class HentaiEra(context: MangaLoaderContext) : "/russian", ) + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) + override fun Element.parseTags() = select("a.tag, .gallery_title a").mapToSet { val key = it.attr("href").removeSuffix('/').substringAfterLast('/') val name = it.selectFirst(".item_name")?.text() ?: it.text() @@ -40,23 +43,33 @@ internal class HentaiEra(context: MangaLoaderContext) : tags: Set?, sortOrder: SortOrder, ): List { - val tag = tags.oneOrThrowIfMany() + if (query.isNullOrEmpty() && tags != null && tags.size > 1) { + return getListPage(page, buildQuery(tags), emptySet(), sortOrder) + } val url = buildString { append("https://") append(domain) if (!tags.isNullOrEmpty()) { - if (tag?.key == "languageKey") { + val tag = tags.single() + if (tag.key == "languageKey") { append("/language") append(tag.title) - append("/?") } else { append("/tag/") - append(tag?.key.orEmpty()) - append("/?") + append(tag.key) + } + append("/") + if (sortOrder == SortOrder.POPULARITY) { + append("popular/") } + append("?") } else if (!query.isNullOrEmpty()) { append("/search/?key=") - append(query.urlEncoded()) + if (sortOrder == SortOrder.POPULARITY) { + append(query.replace("<=1&dl=0&pp=0&tr=0", "<=0&dl=0&pp=1&tr=0")) + } else { + append(query) + } append("&") } else { append("/?") @@ -67,10 +80,51 @@ internal class HentaiEra(context: MangaLoaderContext) : return parseMangaList(webClient.httpGet(url).parseHtml()) } + private fun buildQuery(tags: Collection): String { + val queryDefault = + "&search=&mg=1&dj=1&ws=1&is=1&ac=1&gc=1&en=0&jp=0&es=0&fr=0&kr=0&de=0&ru=0<=1&dl=0&pp=0&tr=0" + var tag = "" + var queryMod = "" + tags.map { + if (it.key == "languageKey" && it.title == "/english") { + queryMod = queryDefault.replace("en=0", "en=1") + } + if (it.key == "languageKey" && it.title == "/japanese") { + queryMod = queryDefault.replace("jp=0", "jp=1") + } + if (it.key == "languageKey" && it.title == "/spanish") { + queryMod = queryDefault.replace("es=0", "es=1") + } + if (it.key == "languageKey" && it.title == "/french") { + queryMod = queryDefault.replace("fr=0", "fr=1") + } + if (it.key == "languageKey" && it.title == "/korean") { + queryMod = queryDefault.replace("kr=0", "kr=1") + } + if (it.key == "languageKey" && it.title == "/russian") { + queryMod = queryDefault.replace("ru=0", "ru=1") + } + if (it.key == "languageKey" && it.title == "/german") { + queryMod = queryDefault.replace("de=0", "de=1") + } + if (it.key != "languageKey") { + tag += it.key + " " + } + } + + if (queryMod.isEmpty()) { + queryMod = "&search=&mg=1&dj=1&ws=1&is=1&ac=1&gc=1&en=1&jp=1&es=1&fr=1&kr=1&de=1&ru=1<=1&dl=0&pp=0&tr=0" + } + return tag + queryMod + } + override suspend fun getDetails(manga: Manga): Manga { val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() val urlChapters = doc.selectFirstOrThrow("#cover a, .cover a, .left_cover a").attr("href") val tag = doc.selectFirst(selectTag)?.parseTags() + val branch = doc.select(selectLanguageChapter).joinToString(separator = " / ") { + it.text() + } return manga.copy( tags = tag.orEmpty(), author = doc.selectFirst(selectAuthor)?.text(), @@ -82,7 +136,7 @@ internal class HentaiEra(context: MangaLoaderContext) : url = urlChapters, scanlator = null, uploadDate = 0, - branch = doc.selectFirst(selectLanguageChapter)?.text(), + branch = branch, source = source, ), ), diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiForce.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiForce.kt index 2c3e8a03..dc5a76c7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiForce.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiForce.kt @@ -5,6 +5,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser import org.koitharu.kotatsu.parsers.util.* +import java.util.EnumSet @MangaSourceParser("HENTAIFORCE", "HentaiForce", type = ContentType.HENTAI) internal class HentaiForce(context: MangaLoaderContext) : @@ -34,6 +35,8 @@ internal class HentaiForce(context: MangaLoaderContext) : "/vietnamese", ) + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) + override suspend fun getPageUrl(page: MangaPage): String { val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml() return doc.selectFirstOrThrow(idImg).src() ?: doc.parseFailed("Image src not found") @@ -45,23 +48,31 @@ internal class HentaiForce(context: MangaLoaderContext) : tags: Set?, sortOrder: SortOrder, ): List { - val tag = tags.oneOrThrowIfMany() + if (query.isNullOrEmpty() && tags != null && tags.size > 1) { + return getListPage(page, buildQuery(tags), emptySet(), sortOrder) + } val url = buildString { append("https://") append(domain) if (!tags.isNullOrEmpty()) { - if (tag?.key == "languageKey") { + val tag = tags.single() + if (tag.key == "languageKey") { append("/language") append(tag.title) - append("/") } else { append("/tag/") - append(tag?.key.orEmpty()) - append("/") + append(tag.key) } + if (sortOrder == SortOrder.POPULARITY) { + append("/popular") + } + append("/") } else if (!query.isNullOrEmpty()) { - append("search?q=") + append("/search?q=") append(query.urlEncoded()) + if (sortOrder == SortOrder.POPULARITY) { + append("&sort=popular") + } append("&page=") } else { append("/page/") @@ -70,4 +81,13 @@ internal class HentaiForce(context: MangaLoaderContext) : } return parseMangaList(webClient.httpGet(url).parseHtml()) } + + private fun buildQuery(tags: Collection) = + tags.joinToString(separator = " ") { tag -> + if (tag.key == "languageKey") { + "language:${tag.title.removePrefix("/")}" + } else { + "tag:${tag.title}" + } + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiFox.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiFox.kt index 3b0cb30f..8bcde87c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiFox.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/HentaiFox.kt @@ -1,10 +1,12 @@ package org.koitharu.kotatsu.parsers.site.galleryadults.all +import org.jsoup.nodes.Element import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser import org.koitharu.kotatsu.parsers.util.* +import java.util.EnumSet @MangaSourceParser("HENTAIFOX", "HentaiFox", type = ContentType.HENTAI) internal class HentaiFox(context: MangaLoaderContext) : @@ -30,33 +32,43 @@ internal class HentaiFox(context: MangaLoaderContext) : "/vietnamese", ) + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) + override suspend fun getListPage( page: Int, query: String?, tags: Set?, sortOrder: SortOrder, ): List { - val tag = tags.oneOrThrowIfMany() + if (query.isNullOrEmpty() && tags != null && tags.size > 1) { + return getListPage(page, buildQuery(tags), emptySet(), sortOrder) + } val url = buildString { append("https://") append(domain) if (!tags.isNullOrEmpty()) { - if (tag?.key == "languageKey") { + val tag = tags.single() + if (tag.key == "languageKey") { append("/language") append(tag.title) } else { append("/tag/") - append(tag?.key.orEmpty()) + append(tag.key) + } + if (sortOrder == SortOrder.POPULARITY) { + append("/popular") } if (page > 1) { append("/pag/") append(page) append("/") } - } else if (!query.isNullOrEmpty()) { append("/search/?q=") append(query.urlEncoded()) + if (sortOrder == SortOrder.POPULARITY) { + append("&sort=popular") + } if (page > 1) { append("&page=") append(page) @@ -75,4 +87,23 @@ internal class HentaiFox(context: MangaLoaderContext) : } return parseMangaList(webClient.httpGet(url).parseHtml()) } + + override fun Element.parseTags() = select("a").mapToSet { + val key = it.attr("href").removeSuffix('/').substringAfterLast('/') + val name = it.selectFirst(".list_tag")?.text() ?: it.html().substringBefore("<") + MangaTag( + key = key, + title = name, + source = source, + ) + } + + private fun buildQuery(tags: Collection) = + tags.joinToString(separator = " ") { tag -> + if (tag.key == "languageKey") { + tag.title.removePrefix("/") + } else { + tag.key + } + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/NHentaiParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/NHentaiParser.kt index 6ac01078..078d5eaf 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/NHentaiParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/galleryadults/all/NHentaiParser.kt @@ -6,6 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser import org.koitharu.kotatsu.parsers.util.* +import java.util.EnumSet @MangaSourceParser("NHENTAI", "NHentai.net", type = ContentType.HENTAI) internal class NHentaiParser(context: MangaLoaderContext) : @@ -14,7 +15,7 @@ internal class NHentaiParser(context: MangaLoaderContext) : override val selectGalleryLink = "a" override val selectGalleryTitle = ".caption" override val pathTagUrl = "/tags/popular?page=" - override val selectTags = "#tag-container a" + override val selectTags = "#tag-container" override val selectTag = ".tag-container:contains(Tags:) span.tags" override val selectAuthor = "#tags div.tag-container:contains(Artists:) span.name" override val selectLanguageChapter = @@ -26,6 +27,8 @@ internal class NHentaiParser(context: MangaLoaderContext) : "/chinese", ) + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY) + override suspend fun getListPage( page: Int, query: String?, @@ -43,18 +46,28 @@ internal class NHentaiParser(context: MangaLoaderContext) : if (tag.key == "languageKey") { append("/language") append(tag.title) - append("/?") } else { append("/tag/") append(tag.key) - append("/?") } + append("/") + if (sortOrder == SortOrder.POPULARITY) { + append("popular") + } + append("?") } else if (!query.isNullOrEmpty()) { append("/search/?q=") append(query.urlEncoded()) + if (sortOrder == SortOrder.POPULARITY) { + append("&sort=popular") + } append("&") } else { - append("/?") + if (sortOrder == SortOrder.POPULARITY) { + append("/?sort=popular&") + } else { + append("/?") + } } append("page=") append(page) @@ -78,7 +91,12 @@ internal class NHentaiParser(context: MangaLoaderContext) : ) } - private fun buildQuery(tags: Collection) = tags.joinToString(separator = " ") { tag -> - "tag:\"${tag.key}\"" - } + private fun buildQuery(tags: Collection) = + tags.joinToString(separator = " ") { tag -> + if (tag.key == "languageKey") { + "language:\"${tag.title.removePrefix("/")}\"" + } else { + "tag:\"${tag.key}\"" + } + } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt index 9d2fdc48..b07bb410 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/YugenMangas.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.parsers.site.pt +import org.json.JSONObject import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.PagedMangaParser @@ -7,14 +8,16 @@ import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.json.mapJSON +import org.koitharu.kotatsu.parsers.util.json.mapJSONIndexed +import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* -@MangaSourceParser("YUGENMANGAS", "YugenMangas.org", "pt") +@MangaSourceParser("YUGENMANGAS", "YugenMangas.net.br", "pt") class YugenMangas(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.YUGENMANGAS, 28) { - override val sortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL, SortOrder.UPDATED) - override val configKeyDomain = ConfigKey.Domain("yugenmangas.org") + override val sortOrders: Set = EnumSet.of(SortOrder.ALPHABETICAL) + override val configKeyDomain = ConfigKey.Domain("yugenmangas.net.br") override suspend fun getListPage( page: Int, @@ -22,114 +25,114 @@ class YugenMangas(context: MangaLoaderContext) : PagedMangaParser(context, Manga tags: Set?, sortOrder: SortOrder, ): List { - if (!query.isNullOrEmpty()) { - if (page > 1) { - return emptyList() - } - val url = buildString { - append("https://") - append(domain) - append("/api/series/list/?query=") - append(query.urlEncoded()) - } - val json = webClient.httpGet(url).parseJsonArray() - return json.mapJSON { j -> - val urlManga = "https://$domain/series/${j.getString("slug")}/" - Manga( - id = generateUid(urlManga), - url = urlManga, - publicUrl = urlManga, - title = j.getString("name"), - coverUrl = "", - altTitle = null, - rating = RATING_UNKNOWN, - tags = emptySet(), - description = null, - state = null, - author = null, - isNsfw = false, - source = source, - ) - } - } else { - val url = buildString { - append("https://") - append(domain) - when (sortOrder) { - SortOrder.ALPHABETICAL -> append("/series") - SortOrder.UPDATED -> append("/updates") - else -> append("/updates") + val json = + if (!query.isNullOrEmpty()) { + if (page > 1) { + return emptyList() } + val url = buildString { + append("https://api.") + append(domain) + append("/api/series/list/?query=") + append(query.urlEncoded()) + } + webClient.httpGet(url).parseJsonArray() + } else { if (page > 1) { - append("?page=") - append(page) + return emptyList() } + val url = buildString { + append("https://api.") + append(domain) + append("/api/all_series/?page=1") + } + webClient.httpGet(url).parseJson().getJSONArray("series") } - val doc = webClient.httpGet(url).parseHtml() - return doc.select(".gallery .mangas-gallery, .container-update-series .card-series-updates").map { div -> - val a = div.selectFirstOrThrow("a") - val href = a.attrAsRelativeUrl("href") - Manga( - id = generateUid(href), - url = href, - publicUrl = a.attrAsAbsoluteUrl("href"), - title = div.selectLastOrThrow(".title-serie, .name-manga").text(), - coverUrl = div.selectFirst("img")?.src().orEmpty(), - altTitle = null, - rating = RATING_UNKNOWN, - tags = emptySet(), - description = null, - state = null, - author = null, - isNsfw = false, - source = source, - ) - } + + return json.mapJSON { j -> + val slug = j.getString("slug") + Manga( + id = generateUid(slug), + url = slug, + publicUrl = slug, + title = j.getString("name"), + coverUrl = j.getString("cover"), + altTitle = null, + rating = RATING_UNKNOWN, + tags = emptySet(), + description = null, + state = null, + author = null, + isNsfw = isNsfwSource, + source = source, + ) } } override suspend fun getDetails(manga: Manga): Manga { - val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml() - val dateFormat = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT) - val chapters = doc.requireElementById("listadecapitulos") + val detailManga = + webClient.httpPost("https://api.$domain/api/serie_details/${manga.url}", emptyMap()).parseJson() + val body = JSONObject() + body.put("serie_slug", manga.url) + val chapterManga = webClient.httpPost("https://api.$domain/api/get_chapters_by_serie/", body).parseJson() + .getJSONArray("chapters") + val dateFormat = SimpleDateFormat("dd/MM/yyyy", sourceLocale) return manga.copy( - description = doc.selectFirst(".sinopse .sinopse")?.html(), - author = doc.selectFirst(".author")?.text(), - coverUrl = doc.selectFirst(".side img")?.src().orEmpty(), - state = doc.selectFirst(".lancamento p")?.let { - when (it.text().lowercase()) { + description = detailManga.getString("synopsis"), + coverUrl = detailManga.getString("cover"), + altTitle = detailManga.getString("alternative_names"), + author = detailManga.getString("author"), + state = detailManga.getString("status")?.let { + when (it) { "ongoing" -> MangaState.ONGOING - "completed", "finished" -> MangaState.FINISHED + "completed" -> MangaState.FINISHED + //"hiatus" -> MangaState.PAUSED else -> null } }, - chapters = chapters.select(".chapter").mapChapters(reversed = true) { i, div -> - val a = div.selectFirstOrThrow("a") - val href = a.attrAsRelativeUrl("href") - val title = div.selectFirstOrThrow(".chapter-title").text() - val dateText = div.selectFirstOrThrow(".chapter-lancado").text() + chapters = chapterManga.mapJSONIndexed { i, j -> + val url = "https://api.$domain/api/serie/${manga.url}/chapter/${j.getString("slug")}/images/imgs/" MangaChapter( - id = generateUid(href), - name = title, - number = i + 1, - url = href, + id = generateUid(url), + name = j.getString("name"), + number = j.getString("name").removePrefix("Capítulo ").toInt(), + url = url, scanlator = null, - uploadDate = dateFormat.tryParse(dateText), + uploadDate = parseChapterDate( + dateFormat, + j.getString("upload_date"), + ), branch = null, source = source, ) - }, + }.sortedBy { it.name }, ) } + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + val d = date?.lowercase() ?: return 0 + return when { + d.endsWith(" atrás") -> parseRelativeDate(date) + else -> dateFormat.tryParse(date) + } + } + + private fun parseRelativeDate(date: String): Long { + val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0 + val cal = Calendar.getInstance() + + return when { + WordSet("dia", "dias").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis + WordSet("hora", "horas").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis + else -> 0 + } + } + override suspend fun getPages(chapter: MangaChapter): List { - val fullUrl = chapter.url.toAbsoluteUrl(domain) - val apiUrl = webClient.httpGet(fullUrl).parseHtml().selectFirstOrThrow("script:containsData(imageUrls)").data() - .substringAfter("apiUrl = \"").substringBefore("\";") - val json = webClient.httpGet(apiUrl.toAbsoluteUrl(domain)).parseJson().getJSONArray("chapter_images") - val pages = ArrayList(json.length()) - for (i in 0 until json.length()) { - val img = "https://media.$domain/${json.getString(i)}" + val jsonPages = webClient.httpPost(chapter.url, emptyMap()).parseJson().getJSONArray("chapter_images") + val pages = ArrayList(jsonPages.length()) + for (i in 0 until jsonPages.length()) { + val img = "https://$domain/${jsonPages.getString(i)}" pages.add( MangaPage( id = generateUid(img),