diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt index 457e834d..e0627d8b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/fmreader/FmreaderParser.kt @@ -145,7 +145,6 @@ internal abstract class FmreaderParser( else -> null } } - val alt = doc.body().selectFirst(selectAlt)?.text()?.replace("Other names", "") val auth = doc.body().selectFirst(selectAut)?.text() manga.copy( diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/LadyestelarScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/LadyestelarScan.kt new file mode 100644 index 00000000..c2943ab9 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/LadyestelarScan.kt @@ -0,0 +1,12 @@ +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.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("LADYESTELARSCAN", "Lady Estelar Scan", "pt") +internal class LadyestelarScan(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.LADYESTELARSCAN, "ladyestelarscan.com.br", 10) { + override val datePattern: String = "dd/MM/yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/PassamaoScan.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/PassamaoScan.kt new file mode 100644 index 00000000..3150f150 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/PassamaoScan.kt @@ -0,0 +1,12 @@ +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.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("PASSAMAOSCAN", "Passamao Scan", "pt") +internal class PassamaoScan(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.PASSAMAOSCAN, "passamaoscan.com") { + override val datePattern: String = "dd/MM/yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/ReMangas.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/ReMangas.kt new file mode 100644 index 00000000..9c41cdd8 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/pt/ReMangas.kt @@ -0,0 +1,12 @@ +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.MangaSource +import org.koitharu.kotatsu.parsers.site.madara.MadaraParser + +@MangaSourceParser("REMANGAS", "Re Mangas", "pt") +internal class ReMangas(context: MangaLoaderContext) : + MadaraParser(context, MangaSource.REMANGAS, "remangas.net") { + override val datePattern = "dd/MM/yyyy" +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/MangaBuddy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/MangaBuddy.kt index a6a134d9..58e5bc0b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/MangaBuddy.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madtheme/en/MangaBuddy.kt @@ -1,10 +1,44 @@ package org.koitharu.kotatsu.parsers.site.madtheme.en +import org.json.JSONObject 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.MangaPage import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.madtheme.MadthemeParser +import org.koitharu.kotatsu.parsers.util.domain +import org.koitharu.kotatsu.parsers.util.generateUid +import org.koitharu.kotatsu.parsers.util.parseFailed +import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow +import org.koitharu.kotatsu.parsers.util.src +import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl +import org.koitharu.kotatsu.parsers.util.toRelativeUrl +import java.util.ArrayList +import java.util.Base64 @MangaSourceParser("MANGABUDDY", "Manga Buddy", "en") internal class MangaBuddy(context: MangaLoaderContext) : - MadthemeParser(context, MangaSource.MANGABUDDY, "mangabuddy.com") + MadthemeParser(context, MangaSource.MANGABUDDY, "mangabuddy.com") { + + override suspend fun getPages(chapter: MangaChapter): List { + val chapterUrl = chapter.url.toAbsoluteUrl(domain) + val docs = webClient.httpGet(chapterUrl).parseHtml() + val script = docs.selectFirstOrThrow("script:containsData(chapImages)") + val images = script.data().substringAfter("'").substringBeforeLast("'").split(",") + val pages = ArrayList(images.size) + for (image in images) { + pages.add( + MangaPage( + id = generateUid(image), + url = image, + preview = null, + source = source, + ), + ) + } + return pages + + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PhenixscansParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PhenixscansParser.kt index fb8ece3b..9e744739 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PhenixscansParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/fr/PhenixscansParser.kt @@ -5,7 +5,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -@MangaSourceParser("PHENIXSCANS", "Phenixscans", "fr") +@MangaSourceParser("PHENIXSCANS", "Phenix Scans", "fr") internal class PhenixscansParser(context: MangaLoaderContext) : MangaReaderParser(context, MangaSource.PHENIXSCANS, "phenixscans.fr", pageSize = 20, searchPageSize = 10) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt index 215464fe..377d568f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/pt/HentaiSsssscanlator.kt @@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -@MangaSourceParser("HENTAISSSSSCANLATOR", "Sssscanlator Hentai", "pt", type = ContentType.HENTAI) +@MangaSourceParser("HENTAISSSSSCANLATOR", "Sss Scanlator Hentai", "pt", type = ContentType.HENTAI) internal class HentaiSsssscanlator(context: MangaLoaderContext) : MangaReaderParser( context, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt new file mode 100644 index 00000000..f82008bb --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/LerMangaOnline.kt @@ -0,0 +1,127 @@ +package org.koitharu.kotatsu.parsers.site.pt + +import org.jsoup.nodes.Document +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.util.* +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("LERMANGAONLINE", "Ler Manga Online", "pt") +class LerMangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.LERMANGAONLINE, 20) { + + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED) + + override val configKeyDomain = ConfigKey.Domain("lermangaonline.com.br") + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + if (!query.isNullOrEmpty()) { + return emptyList() // Research revisits non-manga chapters + } + val tag = tags.oneOrThrowIfMany() + val url = buildString { + append("https://") + append(domain) + append("/") + if (!tags.isNullOrEmpty()) { + append(tag?.key.orEmpty()) + append("/") + } + if (page > 1) { + append("page/") + append(page) + append("/") + } + } + return parseManga(webClient.httpGet(url).parseHtml()) + } + + private fun parseManga(docs: Document): List { + return docs.select(".materias .article").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("section h3").text(), + coverUrl = div.selectFirst("img")?.src().orEmpty(), + altTitle = null, + rating = RATING_UNKNOWN, + tags = emptySet(), + description = null, + state = null, + author = null, + isNsfw = isNsfwSource, + source = source, + ) + } + } + + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain/").parseHtml().requireElementById("sub-menu") + return doc.select("ul.container li a").mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").removePrefix("/"), + title = a.text(), + 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) + return manga.copy( + description = doc.selectFirst(".sinopse")?.html(), + tags = doc.selectFirst(".categorias-blog")?.select("a")?.mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").removePrefix("/"), + title = a.text().ifEmpty { return@mapNotNullToSet null }.toTitleCase(), + source = source, + ) + }.orEmpty(), + chapters = doc.select(".capitulos a").mapChapters(reversed = true) { i, a -> + val href = a.attrAsRelativeUrl("href") + val title = a.selectFirstOrThrow(".capitulo").html().substringBeforeLast(" { + return parseManga(webClient.httpGet(seed.url.toAbsoluteUrl(domain)).parseHtml()) + } + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + return doc.select(".images img").map { img -> + val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt new file mode 100644 index 00000000..cec08dd5 --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/pt/MangaOnline.kt @@ -0,0 +1,145 @@ +package org.koitharu.kotatsu.parsers.site.pt + +import org.koitharu.kotatsu.parsers.MangaLoaderContext +import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.PagedMangaParser +import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.* +import org.koitharu.kotatsu.parsers.util.* +import java.text.SimpleDateFormat +import java.util.* + +@MangaSourceParser("MANGAONLINE", "Manga Online", "pt") +class MangaOnline(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.MANGAONLINE, 20) { + + override val sortOrders: Set = EnumSet.of(SortOrder.UPDATED) + + override val configKeyDomain = ConfigKey.Domain("mangaonline.biz") + + override suspend fun getListPage( + page: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, + ): List { + if (!query.isNullOrEmpty()) { + return emptyList() // Research revisits non-manga chapters + } + val tag = tags.oneOrThrowIfMany() + val url = buildString { + append("https://") + append(domain) + if (!tags.isNullOrEmpty()) { + append("/genero/") + append(tag?.key.orEmpty()) + append("/") + } else { + append("/manga/") + } + if (page > 1) { + append("page/") + append(page) + append("/") + } + } + val doc = webClient.httpGet(url).parseHtml() + return doc.select(".items .item").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(".data h3").text(), + coverUrl = div.selectFirst("img")?.src().orEmpty(), + altTitle = null, + rating = div.selectFirst(".rating")?.ownText()?.toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN, + tags = emptySet(), + description = null, + state = null, + author = null, + isNsfw = isNsfwSource, + source = source, + ) + } + } + + override suspend fun getTags(): Set { + val doc = webClient.httpGet("https://$domain/generos/").parseHtml() + return doc.select(".wp-content p a").mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").removeSuffix("/").substringAfterLast("/", ""), + title = a.text(), + 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) + return manga.copy( + description = doc.selectLastOrThrow(".data p").html(), + tags = doc.selectFirst(".sgeneros")?.select("a")?.mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").removeSuffix("/").substringAfterLast("/", ""), + title = a.text(), + source = source, + ) + }.orEmpty(), + chapters = doc.select(".episodios li a").mapChapters(reversed = true) { i, a -> + val href = a.attrAsRelativeUrl("href") + val title = a.html().substringBeforeLast(" { + val doc = + webClient.httpGet(seed.url.toAbsoluteUrl(domain)).parseHtml().requireElementById("single_relacionados") + return doc.select(".owl-item").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(".reltitle h3").text(), + coverUrl = div.selectFirst("img")?.src().orEmpty(), + altTitle = null, + rating = RATING_UNKNOWN, + tags = emptySet(), + description = null, + state = null, + author = null, + isNsfw = isNsfwSource, + source = source, + ) + } + } + + override suspend fun getPages(chapter: MangaChapter): List { + val fullUrl = chapter.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + return doc.select(".content p img").map { img -> + val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found") + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } + } +} 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 bd893937..45bfdb1b 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 @@ -97,6 +97,13 @@ class YugenMangas(context: MangaLoaderContext) : PagedMangaParser(context, Manga 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()) { + "ongoing" -> MangaState.ONGOING + "completed", "finished" -> MangaState.FINISHED + else -> null + } + }, chapters = chapters.select(".chapter").mapChapters(reversed = true) { i, div -> val a = div.selectFirstOrThrow("a") val href = a.attrAsRelativeUrl("href")