diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaIndoParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaIndoParser.kt index 25203fe1..ab3ef475 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaIndoParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/mangareader/id/ManhwaIndoParser.kt @@ -1,10 +1,15 @@ package org.koitharu.kotatsu.parsers.site.mangareader.id +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import org.jsoup.nodes.Element 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.MangaParserSource import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser -import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* @MangaSourceParser("MANHWAINDO", "ManhwaIndo", "id") @@ -16,23 +21,26 @@ internal class ManhwaIndoParser(context: MangaLoaderContext) : override suspend fun getPages(chapter: MangaChapter): List { val chapterUrl = chapter.url.toAbsoluteUrl(domain) val docs = webClient.httpGet(chapterUrl).parseHtml() - return docs.select(selectPage).mapNotNull { img -> - val url = img.attr("data-src").takeIf { it.isNotBlank() }?.toRelativeUrl(domain) ?: return@mapNotNull null - try { - val response = webClient.httpHead(url) - if (response.headers["Content-Type"]?.startsWith("image/") == true) { - MangaPage( - id = generateUid(url), - url = url, - preview = null, - source = source, - ) - } else { - null - } - } catch (e: Exception) { + return coroutineScope { + docs.select(selectPage).map { img -> + async { fetchPage(img) } + }.awaitAll().filterNotNull() + } + } + + private suspend fun fetchPage(img: Element): MangaPage? = runCatchingCancellable { + val url = img.attrAsRelativeUrlOrNull("data-src") ?: return@runCatchingCancellable null + webClient.httpHead(url).use { response -> + if (response.mimeType?.startsWith("image/") == true) { + MangaPage( + id = generateUid(url), + url = url, + preview = null, + source = source, + ) + } else { null } } - } + }.getOrNull() } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenTranh3Q.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenTranh3Q.kt index 5fbd3aaa..bf75bb99 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenTranh3Q.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyenTranh3Q.kt @@ -10,7 +10,11 @@ import java.text.SimpleDateFormat import java.util.* @MangaSourceParser("TRUYENTRANH3Q", "TruyenTranh3Q", "vi") -internal class TruyenTranh3Q(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.TRUYENTRANH3Q, 42) { +internal class TruyenTranh3Q(context: MangaLoaderContext) : + PagedMangaParser(context, MangaParserSource.TRUYENTRANH3Q, 42) { + + private val relativeTimePattern = Regex("(\\d+)\\s*(phút|giờ|ngày|tuần) trước") + private val absoluteTimePattern = Regex("(\\d{2}-\\d{2}-\\d{4})") override val configKeyDomain = ConfigKey.Domain("truyentranh3q.com") @@ -45,9 +49,9 @@ internal class TruyenTranh3Q(context: MangaLoaderContext) : PagedMangaParser(con append("https://") append(domain) append("/tim-kiem-nang-cao") - + append("?") - + if (page > 1) { append("page=") append(page) @@ -68,38 +72,28 @@ internal class TruyenTranh3Q(context: MangaLoaderContext) : PagedMangaParser(con SortOrder.POPULARITY -> "2" SortOrder.RATING -> "6" else -> "0" - } + }, ) append("&status=") - if (filter.states.isNotEmpty()) { - filter.states.oneOrThrowIfMany()?.let { - append( - when (it) { - MangaState.ONGOING -> "1" - MangaState.FINISHED -> "2" - else -> "0" - } - ) - } - } else { - append("0") - } + append( + when (filter.states.oneOrThrowIfMany()) { + MangaState.ONGOING -> "1" + MangaState.FINISHED -> "2" + else -> "0" + }, + ) append("&country=") - if (filter.types.isNotEmpty()) { - filter.types.oneOrThrowIfMany()?.let { - append( - when (it) { - ContentType.MANGA -> "manga" - ContentType.MANHWA -> "manhwa" - ContentType.MANHUA -> "manhua" - ContentType.OTHER -> "other" - else -> "all" - } - ) - } - } else append("all") + append( + when (filter.types.oneOrThrowIfMany()) { + ContentType.MANGA -> "manga" + ContentType.MANHWA -> "manhwa" + ContentType.MANHUA -> "manhua" + ContentType.OTHER -> "other" + else -> "all" + }, + ) if (filter.tags.isNotEmpty()) { append("&categories=") @@ -112,25 +106,26 @@ internal class TruyenTranh3Q(context: MangaLoaderContext) : PagedMangaParser(con val nsfwTags = setOf("18+", "Adult", "Ecchi", "16+", "NTR", "Smut") return doc.select("ul.list_grid.grid > li").map { element -> val aTag = element.selectFirstOrThrow("h3 a") - val tags = element.select(".genre-item").map { + val tags = element.select(".genre-item").mapToSet { MangaTag( key = it.attr("href").substringAfterLast('-').substringBeforeLast('.'), - title = it.text(), - source = source + title = it.text().toTitleCase(sourceLocale), + source = source, ) - }.toSet() + } - val isNsfw = if (tags.any { it.title in nsfwTags }) true else false + val isNsfw = tags.any { it.title in nsfwTags } + val href = aTag.attrAsRelativeUrl("href") Manga( - id = generateUid(aTag.attr("href")), + id = generateUid(href), title = aTag.text(), altTitle = null, - url = aTag.attrAsRelativeUrl("href"), - publicUrl = aTag.attr("href").toAbsoluteUrl(domain), + url = href, + publicUrl = aTag.attrAsAbsoluteUrl("href"), rating = RATING_UNKNOWN, isNsfw = isNsfw, - coverUrl = element.selectFirst(".book_avatar a img")?.src().orEmpty(), + coverUrl = element.selectFirst(".book_avatar a img")?.src(), tags = tags, state = null, author = null, @@ -144,16 +139,16 @@ internal class TruyenTranh3Q(context: MangaLoaderContext) : PagedMangaParser(con val tags = doc.select("ul.list01 li").mapToSet { MangaTag( key = it.attr("href").substringAfterLast('-').substringBeforeLast('.'), - title = it.text(), + title = it.text().toTitleCase(sourceLocale), source = source, ) } return manga.copy( altTitle = doc.selectFirst("h2.other-name")?.textOrNull(), - author = doc.select("li.author a").text(), + author = doc.selectFirst("li.author a")?.textOrNull(), tags = tags, - description = doc.select("div.story-detail-info").text(), + description = doc.selectFirst("div.story-detail-info")?.html(), state = when (doc.selectFirst(".status p.col-xs-9")?.text()) { "Đang tiến hành" -> MangaState.ONGOING "Hoàn thành" -> MangaState.FINISHED @@ -196,9 +191,6 @@ internal class TruyenTranh3Q(context: MangaLoaderContext) : PagedMangaParser(con private fun parseChapterDate(dateText: String?): Long { if (dateText == null) return 0 - val relativeTimePattern = Regex("(\\d+)\\s*(phút|giờ|ngày|tuần) trước") - val absoluteTimePattern = Regex("(\\d{2}-\\d{2}-\\d{4})") - return when { dateText.contains("phút trước") -> { val match = relativeTimePattern.find(dateText) @@ -244,8 +236,8 @@ internal class TruyenTranh3Q(context: MangaLoaderContext) : PagedMangaParser(con return elements.mapIndexed { index, element -> MangaTag( key = (index + 1).toString(), - title = element.text(), - source = source + title = element.text().toTitleCase(sourceLocale), + source = source, ) }.toSet() }