|
|
|
|
@ -6,6 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
|
|
|
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
|
|
|
|
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.Manga
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaListFilter
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaState
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaTag
|
|
|
|
|
@ -13,73 +14,87 @@ import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.SortOrder
|
|
|
|
|
import org.koitharu.kotatsu.parsers.site.mangabox.MangaboxParser
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.*
|
|
|
|
|
import java.util.EnumSet
|
|
|
|
|
|
|
|
|
|
@MangaSourceParser("MANGAIRO", "MangaIro", "en")
|
|
|
|
|
internal class Mangairo(context: MangaLoaderContext) :
|
|
|
|
|
MangaboxParser(context, MangaSource.MANGAIRO) {
|
|
|
|
|
|
|
|
|
|
override val configKeyDomain = ConfigKey.Domain("w.mangairo.com", "chap.mangairo.com")
|
|
|
|
|
|
|
|
|
|
override val otherDomain = "chap.mangairo.com"
|
|
|
|
|
|
|
|
|
|
override val datePattern = "MMM-dd-yy"
|
|
|
|
|
override val listUrl = "/manga-list"
|
|
|
|
|
override val searchUrl = "/list/search/"
|
|
|
|
|
|
|
|
|
|
override val selectDesc = "div#story_discription p"
|
|
|
|
|
override val selectState = "ul.story_info_right li:contains(Status) a"
|
|
|
|
|
override val selectAlt = "ul.story_info_right li:contains(Alter) h2"
|
|
|
|
|
override val selectAut = "ul.story_info_right li:contains(Author) a"
|
|
|
|
|
override val selectTag = "ul.story_info_right li:contains(Genres) a"
|
|
|
|
|
|
|
|
|
|
override val selectChapter = "div.chapter_list li"
|
|
|
|
|
override val selectDate = "p"
|
|
|
|
|
|
|
|
|
|
override val selectPage = "div.panel-read-story img"
|
|
|
|
|
|
|
|
|
|
override suspend fun getListPage(
|
|
|
|
|
page: Int,
|
|
|
|
|
query: String?,
|
|
|
|
|
tags: Set<MangaTag>?,
|
|
|
|
|
sortOrder: SortOrder,
|
|
|
|
|
): List<Manga> {
|
|
|
|
|
val tag = tags.oneOrThrowIfMany()
|
|
|
|
|
override val availableSortOrders: Set<SortOrder> = EnumSet.of(
|
|
|
|
|
SortOrder.UPDATED,
|
|
|
|
|
SortOrder.POPULARITY,
|
|
|
|
|
SortOrder.NEWEST,
|
|
|
|
|
)
|
|
|
|
|
override val isMultipleTagsSupported = false
|
|
|
|
|
override suspend fun getListPage(page: Int, filter: MangaListFilter?): List<Manga> {
|
|
|
|
|
val url = buildString {
|
|
|
|
|
append("https://")
|
|
|
|
|
append(domain)
|
|
|
|
|
when (filter) {
|
|
|
|
|
|
|
|
|
|
if (!query.isNullOrEmpty()) {
|
|
|
|
|
append(searchUrl)
|
|
|
|
|
append(query.urlEncoded())
|
|
|
|
|
append("?page=")
|
|
|
|
|
append(page.toString())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
is MangaListFilter.Search -> {
|
|
|
|
|
append(searchUrl)
|
|
|
|
|
append(filter.query.urlEncoded())
|
|
|
|
|
append("?page=")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
append("$listUrl/")
|
|
|
|
|
is MangaListFilter.Advanced -> {
|
|
|
|
|
append(listUrl)
|
|
|
|
|
append("/type-")
|
|
|
|
|
when (filter.sortOrder) {
|
|
|
|
|
SortOrder.POPULARITY -> append("topview")
|
|
|
|
|
SortOrder.UPDATED -> append("latest")
|
|
|
|
|
SortOrder.NEWEST -> append("newest")
|
|
|
|
|
else -> append("latest")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
append("/type-")
|
|
|
|
|
when (sortOrder) {
|
|
|
|
|
SortOrder.POPULARITY -> append("topview")
|
|
|
|
|
SortOrder.UPDATED -> append("latest")
|
|
|
|
|
SortOrder.NEWEST -> append("newest")
|
|
|
|
|
else -> append("latest")
|
|
|
|
|
append("/ctg-")
|
|
|
|
|
if (filter.tags.isNotEmpty()) {
|
|
|
|
|
filter.tags.oneOrThrowIfMany()?.let {
|
|
|
|
|
append(it.key)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
append("all")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
append("/state-")
|
|
|
|
|
if (filter.states.isNotEmpty()) {
|
|
|
|
|
filter.states.oneOrThrowIfMany()?.let {
|
|
|
|
|
append(
|
|
|
|
|
when (it) {
|
|
|
|
|
MangaState.ONGOING -> "ongoing"
|
|
|
|
|
MangaState.FINISHED -> "completed"
|
|
|
|
|
else -> "all"
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
append("all")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
append("/page-")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!tags.isNullOrEmpty()) {
|
|
|
|
|
append("/ctg-")
|
|
|
|
|
append(tag?.key.orEmpty())
|
|
|
|
|
} else {
|
|
|
|
|
append("/ctg-all")
|
|
|
|
|
null -> {
|
|
|
|
|
append(listUrl)
|
|
|
|
|
append("/type-latest/ctg-all/state-all/page-")
|
|
|
|
|
}
|
|
|
|
|
append("/state-all/page-")
|
|
|
|
|
append(page.toString())
|
|
|
|
|
}
|
|
|
|
|
append(page.toString())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val doc = webClient.httpGet(url).parseHtml()
|
|
|
|
|
|
|
|
|
|
return doc.select("div.story-item").map { div ->
|
|
|
|
|
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
|
|
|
|
Manga(
|
|
|
|
|
@ -115,13 +130,9 @@ internal class Mangairo(context: MangaLoaderContext) :
|
|
|
|
|
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
|
|
|
|
val fullUrl = manga.url.toAbsoluteUrl(domain)
|
|
|
|
|
val doc = webClient.httpGet(fullUrl).parseHtml()
|
|
|
|
|
|
|
|
|
|
val chaptersDeferred = async { getChapters(doc) }
|
|
|
|
|
|
|
|
|
|
val desc = doc.selectFirstOrThrow(selectDesc).html()
|
|
|
|
|
|
|
|
|
|
val stateDiv = doc.select(selectState).text()
|
|
|
|
|
|
|
|
|
|
val state = stateDiv.let {
|
|
|
|
|
when (it) {
|
|
|
|
|
in ongoing -> MangaState.ONGOING
|
|
|
|
|
@ -131,9 +142,7 @@ internal class Mangairo(context: MangaLoaderContext) :
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val alt = doc.body().select(selectAlt).text().replace("Alternative : ", "")
|
|
|
|
|
|
|
|
|
|
val aut = doc.body().select(selectAut).eachText().joinToString()
|
|
|
|
|
|
|
|
|
|
manga.copy(
|
|
|
|
|
tags = doc.body().select(selectTag).mapNotNullToSet { a ->
|
|
|
|
|
MangaTag(
|
|
|
|
|
@ -151,6 +160,4 @@ internal class Mangairo(context: MangaLoaderContext) :
|
|
|
|
|
isNsfw = manga.isNsfw,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|