From d1a12df18f49aa4a3d0833c5bfaca592e51ca42a Mon Sep 17 00:00:00 2001 From: Draken <131387159+dragonx943@users.noreply.github.com> Date: Thu, 1 Aug 2024 00:42:27 +0700 Subject: [PATCH] Create Saytruyenhay.kt --- .../parsers/site/madara/vi/Saytruyenhay.kt | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Saytruyenhay.kt diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Saytruyenhay.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Saytruyenhay.kt new file mode 100644 index 00000000..f8f23d0e --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Saytruyenhay.kt @@ -0,0 +1,166 @@ +package org.koitharu.kotatsu.parsers.site.madara.vi + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +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.* +import java.util.* + +@Broken +@MangaSourceParser("SAYTRUYENHAY", "PheTruyen", "vi") +internal class Saytruyenhay(context: MangaLoaderContext) : + MadaraParser(context, MangaParserSource.SAYTRUYENHAY, "phetruyen.vip", 40) { + + override val tagPrefix = "genre/" + override val withoutAjax = true + override val listUrl = "public/genre/manga/" + override val availableStates: Set = emptySet() + override val availableContentRating: Set = emptySet() + override val availableSortOrders: Set = + EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.RATING, SortOrder.NEWEST) + + init { + paginator.firstPage = 1 + searchPaginator.firstPage = 1 + } + + override suspend fun getListPage(page: Int, filter: MangaListFilter?): List { + val url = buildString { + append("https://") + append(domain) + when (filter) { + is MangaListFilter.Search -> { + append("/search?s=") + append(filter.query.urlEncoded()) + append("&page=") + append(page.toString()) + } + + is MangaListFilter.Advanced -> { + + val tag = filter.tags.oneOrThrowIfMany() + if (filter.tags.isNotEmpty()) { + append("/$tagPrefix") + append(tag?.key.orEmpty()) + } else { + append("/$listUrl") + + } + append("?page=") + append(page.toString()) + append("&m_orderby=") + when (filter.sortOrder) { + SortOrder.UPDATED -> append("latest") + SortOrder.RATING -> append("rating") + SortOrder.POPULARITY -> append("views") + SortOrder.NEWEST -> append("new") + else -> append("latest") + } + } + + null -> { + append("/$listUrl") + append("?page=") + append(page.toString()) + } + } + } + + val doc = webClient.httpGet(url).parseHtml() + + + return doc.select("div.row.c-tabs-item__content").ifEmpty { + doc.select("div.page-item-detail") + }.map { div -> + val href = div.selectFirst("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"))?.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() + ?.lowercase().orEmpty()) { + in ongoing -> MangaState.ONGOING + in finished -> MangaState.FINISHED + else -> null + }, + source = source, + isNsfw = isNsfwSource, + ) + } + } + + override suspend fun getDetails(manga: Manga): Manga = coroutineScope { + val fullUrl = manga.url.toAbsoluteUrl(domain) + val doc = webClient.httpGet(fullUrl).parseHtml() + val body = doc.body() + + val chaptersDeferred = async { getChapters(manga, doc) } + + val desc = body.select(selectDesc).let { + if (it.select("p").text().isNotEmpty()) { + it.select("p").joinToString(separator = "\n\n") { p -> + p.text().replace("
", "\n") + } + } else { + it.text() + } + } + + val stateDiv = (body.selectFirst("div.post-content_item:contains(Status)") + ?: body.selectFirst("div.post-content_item:contains(Statut)") + ?: body.selectFirst("div.post-content_item:contains(État)") + ?: body.selectFirst("div.post-content_item:contains(حالة العمل)") + ?: body.selectFirst("div.post-content_item:contains(Estado)") + ?: body.selectFirst("div.post-content_item:contains(สถานะ)") + ?: body.selectFirst("div.post-content_item:contains(Stato)") + ?: body.selectFirst("div.post-content_item:contains(Durum)") + ?: body.selectFirst("div.post-content_item:contains(Statüsü)") + ?: body.selectFirst("div.post-content_item:contains(Статус)") + ?: body.selectFirst("div.post-content_item:contains(状态)") + ?: body.selectFirst("div.post-content_item:contains(الحالة)"))?.selectLast("div.summary-content") + + val state = stateDiv?.let { + when (it.text()) { + in ongoing -> MangaState.ONGOING + in finished -> MangaState.FINISHED + else -> null + } + } + + val alt = + doc.body().select(".post-content_item:contains(Alt) .summary-content").firstOrNull()?.tableValue()?.text() + ?.trim() ?: doc.body().select(".post-content_item:contains(Nomes alternativos: ) .summary-content") + .firstOrNull()?.tableValue()?.text()?.trim() + + manga.copy( + tags = doc.body().select(selectGenre).mapNotNullToSet { a -> + MangaTag( + key = a.attr("href").removeSuffix("/").substringAfterLast('/'), + title = a.text().toTitleCase(), + source = source, + ) + }, + description = desc, + altTitle = alt, + state = state, + chapters = chaptersDeferred.await(), + ) + } + +}