[Kiutaku + Xiutaku] Add sources (#1755)

Co-authored-by: Draken <dragonx943@users.noreply.github.com>
master
Draken 12 months ago committed by GitHub
parent 8f61beadb2
commit cc98931147
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1 +1 @@
total: 1218 total: 1220

@ -0,0 +1,130 @@
package org.koitharu.kotatsu.parsers.site.gallery
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.core.LegacyMangaParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*
internal abstract class GalleryParser(
context: MangaLoaderContext,
source: MangaParserSource,
domain: String
) : LegacyMangaParser(context, source) {
override val configKeyDomain = ConfigKey.Domain(domain)
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override val availableSortOrders: Set<SortOrder>
get() = EnumSet.of(SortOrder.NEWEST, SortOrder.POPULARITY)
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isSearchSupported = true,
)
override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions()
override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = urlBuilder().apply {
when {
!filter.query.isNullOrEmpty() -> addQueryParameter("search", filter.query)
filter.tags.isNotEmpty() -> addPathSegments(filter.tags.first().key)
order == SortOrder.POPULARITY -> addPathSegment("hot")
}
addQueryParameter("start", offset.toString())
}.build()
val content = webClient.httpGet(url).parseHtml()
val currentPage = content.selectFirst("a.pagination-link.is-current")?.text()?.toIntOrNull()
val titlePage = content.selectFirst("head > title")?.text()
?.substringAfter("page ", "")
?.substringBefore(" ", "")
?.toIntOrNull()
if (titlePage != null && currentPage != titlePage) return emptyList()
return content.select("div.items-row").map { el ->
val titleEl = el.selectFirstOrThrow("div.page-header a.item-link")
val relUrl = titleEl.attrOrThrow("href")
Manga(
id = generateUid(relUrl),
url = relUrl,
title = titleEl.text(),
altTitles = emptySet(),
publicUrl = relUrl.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
contentRating = ContentRating.ADULT,
coverUrl = el.selectFirst("div.item-thumb img")?.attr("src"),
tags = el.select("div.item-tags > a.tag").mapNotNullToSet { tagEl ->
MangaTag(
title = tagEl.text(),
key = tagEl.attrAsRelativeUrlOrNull("href")
?.removePrefix("/") ?: return@mapNotNullToSet null,
source = source,
)
},
state = MangaState.FINISHED,
authors = emptySet(),
largeCoverUrl = null,
description = null,
chapters = null,
source = source,
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val content = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val description = content.selectFirst("div.article-info")?.text()?.trim() ?: ""
val df = SimpleDateFormat("HH:mm dd-MM-yyyy")
val time = content.selectFirst("div.article-info > small")?.text()?.trim()
val chapters = content.selectFirst("nav.pagination")?.select("a.pagination-link")
?.mapChapters { index, element ->
val relUrl = element.attrAsRelativeUrl("href")
MangaChapter(
id = generateUid(relUrl),
title = null,
number = index + 1f,
volume = 0,
url = relUrl,
scanlator = null,
uploadDate = df.tryParse(time),
branch = null,
source = source,
)
}.orEmpty()
return manga.copy(chapters = chapters, description = description)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val content = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
return content.selectFirstOrThrow("div.article-fulltext").select("p > img").mapNotNull { el ->
val url = el.attrOrNull("src") ?: return@mapNotNull null
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source,
)
}
}
protected suspend fun fetchTags(): Set<MangaTag> {
val root = webClient.httpGet("https://$domain").parseHtml()
return root.select("div#navbar-main a.navbar-item").map { a ->
MangaTag(
title = a.text(),
key = a.attr("href").removePrefix("/"),
source = source,
)
}.toSet()
}
}

@ -0,0 +1,15 @@
package org.koitharu.kotatsu.parsers.site.gallery.all
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions
import org.koitharu.kotatsu.parsers.site.gallery.GalleryParser
import org.koitharu.kotatsu.parsers.Broken
@Broken("Blocked by Cloudflare")
@MangaSourceParser("KIUTAKU", "Kiutaku", type = ContentType.OTHER)
internal class Kiutaku(context: MangaLoaderContext) :
GalleryParser(context, MangaParserSource.KIUTAKU, "kiutaku.com")

@ -0,0 +1,18 @@
package org.koitharu.kotatsu.parsers.site.gallery.vi
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.site.gallery.GalleryParser
@MangaSourceParser("BUONDUA", "Buon Dua", "vi", type = ContentType.OTHER)
internal class BuonDua(context: MangaLoaderContext) :
GalleryParser(context, MangaParserSource.BUONDUA, "buondua.com") {
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain(
"buondua.com",
"buondua.us",
)
}

@ -0,0 +1,18 @@
package org.koitharu.kotatsu.parsers.site.gallery.zh
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions
import org.koitharu.kotatsu.parsers.site.gallery.GalleryParser
@MangaSourceParser("XIUTAKU", "Xiutaku", "zh", type = ContentType.OTHER)
internal class Xiutaku(context: MangaLoaderContext) :
GalleryParser(context, MangaParserSource.XIUTAKU, "xiutaku.com") {
override suspend fun getFilterOptions():
MangaListFilterOptions = MangaListFilterOptions(availableTags = fetchTags())
}

@ -1,114 +0,0 @@
package org.koitharu.kotatsu.parsers.site.vi
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.core.LegacyMangaParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*
@MangaSourceParser("BUONDUA", "Buon Dua", type = ContentType.OTHER)
internal class BuonDuaParser(context: MangaLoaderContext) : LegacyMangaParser(context, MangaParserSource.BUONDUA) {
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain(
"buondua.com",
"buondua.us",
)
override val availableSortOrders: Set<SortOrder>
get() = EnumSet.of(SortOrder.NEWEST, SortOrder.POPULARITY)
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isSearchSupported = true,
)
override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions()
override suspend fun getDetails(manga: Manga): Manga {
val content = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val df = SimpleDateFormat("HH:mm dd-MM-yyyy")
val time = content.selectFirst("div.article-info > small")?.text()?.trim()
val chapters = content.selectFirst("nav.pagination")?.select("a.pagination-link")
?.mapChapters { index, element ->
val relUrl = element.attrAsRelativeUrl("href")
MangaChapter(
id = generateUid(relUrl),
title = null,
number = index + 1f,
volume = 0,
url = relUrl,
scanlator = null,
uploadDate = df.tryParse(time),
branch = null,
source = source,
)
}.orEmpty()
return manga.copy(chapters = chapters)
}
override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = urlBuilder().apply {
when {
!filter.query.isNullOrEmpty() -> addQueryParameter("search", filter.query)
filter.tags.isNotEmpty() -> addPathSegments(filter.tags.first().key)
order == SortOrder.POPULARITY -> addPathSegment("hot")
}
addQueryParameter("start", offset.toString())
}.build()
val content = webClient.httpGet(url).parseHtml()
val currentPage = content.selectFirst("a.pagination-link.is-current")?.text()?.toIntOrNull()
val titlePage = content.selectFirst("head > title")?.text()
?.substringAfter("page ", "")
?.substringBefore(" ", "")
?.toIntOrNull()
if (titlePage != null && currentPage != titlePage) return emptyList()
return content.select("div.items-row").map { el ->
val titleEl = el.selectFirstOrThrow("div.page-header a.item-link")
val relUrl = titleEl.attrOrThrow("href")
Manga(
id = generateUid(relUrl),
url = relUrl,
title = titleEl.text(),
altTitles = emptySet(),
publicUrl = relUrl.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
contentRating = ContentRating.ADULT,
coverUrl = el.selectFirst("div.item-thumb img")?.attr("src"),
tags = el.select("div.item-tags > a.tag").mapNotNullToSet { tagEl ->
MangaTag(
title = tagEl.text(),
key = tagEl.attrAsRelativeUrlOrNull("href")
?.removePrefix("/") ?: return@mapNotNullToSet null,
source = source,
)
},
state = MangaState.FINISHED,
authors = emptySet(),
largeCoverUrl = null,
description = null,
chapters = null,
source = source,
)
}
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val content = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
return content.selectFirstOrThrow("div.article-fulltext").select("p > img").mapNotNull { el ->
val url = el.attrOrNull("src") ?: return@mapNotNull null
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source,
)
}
}
}
Loading…
Cancel
Save