[AsuraComic] Fix close #1077

add ContentTypes , SearchWithFilters
[CloneManga] isSearchSupported false
[MangaGeko] fix tags,  add MultipleTags, TagsExclusion
[Manhwa18.net] add isSearchSupported
[ManhwasMen] add Exception
[Pururin] add SearchWithFilters, MultipleTags , TagsExclusion
[VyManga] add SearchWithFilters, MultipleTags, TagsExclusion
master
devi 2 years ago
parent d1f9b0d829
commit f1939391e8

@ -17,32 +17,38 @@ import java.util.*
internal class AsuraScansParser(context: MangaLoaderContext) : internal class AsuraScansParser(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.ASURASCANS, pageSize = 30) { PagedMangaParser(context, MangaParserSource.ASURASCANS, pageSize = 30) {
override val configKeyDomain = ConfigKey.Domain("asuracomic.net")
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override val availableSortOrders: Set<SortOrder> = EnumSet.of( override val availableSortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.RATING, SortOrder.RATING,
SortOrder.UPDATED, SortOrder.UPDATED,
SortOrder.NEWEST, SortOrder.POPULARITY,
SortOrder.ALPHABETICAL_DESC, SortOrder.ALPHABETICAL_DESC,
SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL,
) )
override val configKeyDomain = ConfigKey.Domain("asuracomic.net")
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isMultipleTagsSupported = true, isMultipleTagsSupported = true,
isSearchSupported = true, isSearchSupported = true,
isSearchWithFiltersSupported = true,
) )
override suspend fun getFilterOptions() = MangaListFilterOptions( override suspend fun getFilterOptions() = MangaListFilterOptions(
availableTags = getOrCreateTagMap().values.toSet(), availableTags = getOrCreateTagMap().values.toSet(),
availableStates = EnumSet.allOf(MangaState::class.java), availableStates = EnumSet.allOf(MangaState::class.java),
availableContentTypes = EnumSet.of(
ContentType.MANGA,
ContentType.MANHWA,
ContentType.MANHUA,
),
) )
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> { override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = buildString { val url = buildString {
append("https://") append("https://")
@ -50,14 +56,11 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
append("/series?page=") append("/series?page=")
append(page) append(page)
when { filter.query?.let {
!filter.query.isNullOrEmpty() -> {
append("&name=") append("&name=")
append(filter.query.urlEncoded()) append(filter.query.urlEncoded())
} }
else -> {
if (filter.tags.isNotEmpty()) { if (filter.tags.isNotEmpty()) {
append("&genres=") append("&genres=")
append(filter.tags.joinToString(separator = ",") { it.key }) append(filter.tags.joinToString(separator = ",") { it.key })
@ -76,18 +79,28 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
) )
} }
append("&types=-1&order=") filter.types.oneOrThrowIfMany()?.let {
append("&types=")
append(
when (it) {
ContentType.MANGA -> "3"
ContentType.MANHWA -> "1"
ContentType.MANHUA -> "2"
else -> ""
},
)
}
append("&order=")
when (order) { when (order) {
SortOrder.RATING -> append("rating") SortOrder.RATING -> append("rating")
SortOrder.UPDATED -> append("update") SortOrder.UPDATED -> append("update")
SortOrder.NEWEST -> append("latest") SortOrder.POPULARITY -> append("bookmarks")
SortOrder.ALPHABETICAL_DESC -> append("desc") SortOrder.ALPHABETICAL_DESC -> append("desc")
SortOrder.ALPHABETICAL -> append("asc") SortOrder.ALPHABETICAL -> append("asc")
else -> append("update") else -> append("update")
} }
} }
}
}
val doc = webClient.httpGet(url).parseHtml() val doc = webClient.httpGet(url).parseHtml()
return doc.select("div.grid > a[href]").map { a -> return doc.select("div.grid > a[href]").map { a ->
val href = "/" + a.attrAsRelativeUrl("href") val href = "/" + a.attrAsRelativeUrl("href")
@ -101,7 +114,7 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
rating = a.selectFirst("div.block label.ml-1")?.text()?.toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN, rating = a.selectFirst("div.block label.ml-1")?.text()?.toFloatOrNull()?.div(10f) ?: RATING_UNKNOWN,
tags = emptySet(), tags = emptySet(),
author = null, author = null,
state = when (a.selectLastOrThrow("span.status").text()) { state = when (a.selectLast("span.status")?.text().orEmpty()) {
"Ongoing" -> MangaState.ONGOING "Ongoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED "Completed" -> MangaState.FINISHED
"Hiatus" -> MangaState.PAUSED "Hiatus" -> MangaState.PAUSED

@ -18,10 +18,7 @@ internal class CloneMangaParser(context: MangaLoaderContext) :
override val configKeyDomain = ConfigKey.Domain("manga.clone-army.org") override val configKeyDomain = ConfigKey.Domain("manga.clone-army.org")
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities()
get() = MangaListFilterCapabilities(
isSearchSupported = true,
)
override suspend fun getFilterOptions() = MangaListFilterOptions() override suspend fun getFilterOptions() = MangaListFilterOptions()
@ -31,9 +28,6 @@ internal class CloneMangaParser(context: MangaLoaderContext) :
} }
override suspend fun getList(order: SortOrder, filter: MangaListFilter): List<Manga> { override suspend fun getList(order: SortOrder, filter: MangaListFilter): List<Manga> {
if (!filter.query.isNullOrEmpty()) {
return emptyList()
}
val doc = webClient.httpGet("https://$domain/viewer_landing.php").parseHtml() val doc = webClient.httpGet("https://$domain/viewer_landing.php").parseHtml()
val mangas = doc.getElementsByClass("comicPreviewContainer") val mangas = doc.getElementsByClass("comicPreviewContainer")
return mangas.mapNotNull { item -> return mangas.mapNotNull { item ->

@ -14,9 +14,6 @@ import java.util.*
@MangaSourceParser("COMICEXTRA", "ComicExtra", "en") @MangaSourceParser("COMICEXTRA", "ComicExtra", "en")
internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.COMICEXTRA, 25) { internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.COMICEXTRA, 25) {
override val availableSortOrders: Set<SortOrder> =
EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.NEWEST)
override val configKeyDomain = ConfigKey.Domain("comixextra.com") override val configKeyDomain = ConfigKey.Domain("comixextra.com")
override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)
@ -26,6 +23,9 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
isSearchSupported = true, isSearchSupported = true,
) )
override val availableSortOrders: Set<SortOrder> =
EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.NEWEST)
override suspend fun getFilterOptions() = MangaListFilterOptions( override suspend fun getFilterOptions() = MangaListFilterOptions(
availableTags = fetchAvailableTags(), availableTags = fetchAvailableTags(),
availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED), availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED),
@ -52,6 +52,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
} }
else -> { else -> {
if (filter.tags.isNotEmpty() && filter.states.isEmpty()) { if (filter.tags.isNotEmpty() && filter.states.isEmpty()) {
filter.tags.oneOrThrowIfMany()?.let { filter.tags.oneOrThrowIfMany()?.let {
append(it.key) append(it.key)
@ -67,7 +68,9 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
) )
} }
} else if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) { }
if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) {
throw IllegalArgumentException(ErrorMessages.FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED) throw IllegalArgumentException(ErrorMessages.FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED)
} else { } else {
when (order) { when (order) {

@ -20,11 +20,18 @@ import java.util.*
@MangaSourceParser("DYNASTYSCANS", "DynastyScans", "en") @MangaSourceParser("DYNASTYSCANS", "DynastyScans", "en")
internal class DynastyScans(context: MangaLoaderContext) : internal class DynastyScans(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.DYNASTYSCANS, 117) { PagedMangaParser(context, MangaParserSource.DYNASTYSCANS, 117) {
override val availableSortOrders: Set<SortOrder> = EnumSet.of(SortOrder.ALPHABETICAL)
override val configKeyDomain = ConfigKey.Domain("dynasty-scans.com") override val configKeyDomain = ConfigKey.Domain("dynasty-scans.com")
override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP) override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override val availableSortOrders: Set<SortOrder> = EnumSet.of(SortOrder.ALPHABETICAL)
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isSearchSupported = true, isSearchSupported = true,
@ -34,11 +41,6 @@ internal class DynastyScans(context: MangaLoaderContext) :
availableTags = fetchAvailableTags(), availableTags = fetchAvailableTags(),
) )
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> { override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
when { when {
!filter.query.isNullOrEmpty() -> { !filter.query.isNullOrEmpty() -> {
@ -67,7 +69,6 @@ internal class DynastyScans(context: MangaLoaderContext) :
append("?view=groupings") append("?view=groupings")
} else { } else {
append("/series?view=cover") append("/series?view=cover")
} }
append("&page=") append("&page=")
@ -78,7 +79,6 @@ internal class DynastyScans(context: MangaLoaderContext) :
} }
} }
private fun parseMangaList(doc: Document): List<Manga> { private fun parseMangaList(doc: Document): List<Manga> {
return doc.select("li.span2") return doc.select("li.span2")
.map { div -> .map { div ->

@ -3,6 +3,7 @@ package org.koitharu.kotatsu.parsers.site.en
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import org.json.JSONArray import org.json.JSONArray
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.ErrorMessages import org.koitharu.kotatsu.parsers.ErrorMessages
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
@ -15,6 +16,7 @@ import org.koitharu.kotatsu.parsers.util.json.toJSONList
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@Broken
@MangaSourceParser("FLIXSCANSORG", "FlixScans.org", "en") @MangaSourceParser("FLIXSCANSORG", "FlixScans.org", "en")
internal class FlixScansOrg(context: MangaLoaderContext) : internal class FlixScansOrg(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.FLIXSCANSORG, 18) { PagedMangaParser(context, MangaParserSource.FLIXSCANSORG, 18) {

@ -22,6 +22,8 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isSearchSupported = true, isSearchSupported = true,
isMultipleTagsSupported = true,
isTagsExclusionSupported = true,
) )
override suspend fun getFilterOptions() = MangaListFilterOptions( override suspend fun getFilterOptions() = MangaListFilterOptions(
@ -51,20 +53,24 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
append("/browse-comics/?results=") append("/browse-comics/?results=")
append(page) append(page)
if (filter.tags.isNotEmpty()) {
append("&tags_include=")
append(filter.tags.joinToString(separator = ",") { it.key })
}
if (filter.tagsExclude.isNotEmpty()) {
append("&tags_exclude=")
append(filter.tagsExclude.joinToString(separator = ",") { it.key })
}
append("&filter=") append("&filter=")
when (order) { when (order) {
SortOrder.POPULARITY -> append("views") SortOrder.POPULARITY -> append("views")
SortOrder.UPDATED -> append("Updated") SortOrder.UPDATED -> append("Updated")
SortOrder.NEWEST -> append("New") SortOrder.NEWEST -> append("New")
// SortOrder.RANDOM -> append("Random")
else -> append("Updated") else -> append("Updated")
} }
if (filter.tags.isNotEmpty()) {
filter.tags.oneOrThrowIfMany()?.let {
append("&genre=")
append(it.key)
}
}
} }
} }
} }
@ -90,7 +96,7 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
private suspend fun fetchAvailableTags(): Set<MangaTag> { private suspend fun fetchAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain/browse-comics/").parseHtml() val doc = webClient.httpGet("https://$domain/browse-comics/").parseHtml()
return doc.select("label.checkbox-inline").mapNotNullToSet { label -> return doc.selectFirstOrThrow("div.genre-select-i").select("label").mapNotNullToSet { label ->
MangaTag( MangaTag(
key = label.selectFirstOrThrow("input").attr("value"), key = label.selectFirstOrThrow("input").attr("value"),
title = label.text(), title = label.text(),

@ -15,11 +15,16 @@ import java.util.*
internal class MangaKawaiiEn(context: MangaLoaderContext) : internal class MangaKawaiiEn(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.MANGAKAWAII_EN, 50) { PagedMangaParser(context, MangaParserSource.MANGAKAWAII_EN, 50) {
override val configKeyDomain = ConfigKey.Domain("www.mangakawaii.io")
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override val availableSortOrders: Set<SortOrder> = override val availableSortOrders: Set<SortOrder> =
EnumSet.of(SortOrder.UPDATED, SortOrder.ALPHABETICAL) EnumSet.of(SortOrder.UPDATED, SortOrder.ALPHABETICAL)
override val configKeyDomain = ConfigKey.Domain("www.mangakawaii.io")
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isSearchSupported = true, isSearchSupported = true,
@ -29,11 +34,6 @@ internal class MangaKawaiiEn(context: MangaLoaderContext) :
availableTags = fetchAvailableTags(), availableTags = fetchAvailableTags(),
) )
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override fun getRequestHeaders(): Headers = Headers.Builder() override fun getRequestHeaders(): Headers = Headers.Builder()
.add("Accept-Language", "en") .add("Accept-Language", "en")
.build() .build()

@ -29,13 +29,12 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
SortOrder.RATING, SortOrder.RATING,
) )
private val tagsMap = SuspendLazy(::parseTags)
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isMultipleTagsSupported = true, isMultipleTagsSupported = true,
isTagsExclusionSupported = true, isTagsExclusionSupported = true,
isSearchSupported = true, isSearchSupported = true,
isSearchWithFiltersSupported = true,
) )
override suspend fun getFilterOptions() = MangaListFilterOptions( override suspend fun getFilterOptions() = MangaListFilterOptions(
@ -63,14 +62,11 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
append("/tim-kiem?page=") append("/tim-kiem?page=")
append(page.toString()) append(page.toString())
when { filter.query?.let {
!filter.query.isNullOrEmpty() -> {
append("&q=") append("&q=")
append(filter.query.urlEncoded()) append(filter.query.urlEncoded())
} }
else -> {
append("&accept_genres=") append("&accept_genres=")
if (filter.tags.isNotEmpty()) { if (filter.tags.isNotEmpty()) {
append( append(
@ -94,7 +90,7 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
SortOrder.UPDATED -> "update" SortOrder.UPDATED -> "update"
SortOrder.NEWEST -> "new" SortOrder.NEWEST -> "new"
SortOrder.RATING -> "like" SortOrder.RATING -> "like"
else -> null else -> "update"
}, },
) )
@ -109,8 +105,14 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
}, },
) )
} }
}
} // Support author
// filter.author.let{
// the
// append("&artist=")
// append(filter.author)
// }
} }
val docs = webClient.httpGet(url).parseHtml() val docs = webClient.httpGet(url).parseHtml()
@ -184,14 +186,8 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
) )
} }
// 7 minutes ago
// 5 hours ago
// 2 days ago
// 2 weeks ago
// 4 years ago
private fun parseUploadDate(timeStr: String?): Long { private fun parseUploadDate(timeStr: String?): Long {
timeStr ?: return 0 timeStr ?: return 0
val timeWords = timeStr.split(' ') val timeWords = timeStr.split(' ')
if (timeWords.size != 3) return 0 if (timeWords.size != 3) return 0
val timeWord = timeWords[1] val timeWord = timeWords[1]
@ -226,6 +222,8 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
} }
} }
private val tagsMap = SuspendLazy(::parseTags)
private suspend fun parseTags(): Map<String, MangaTag> { private suspend fun parseTags(): Map<String, MangaTag> {
val doc = webClient.httpGet("https://$domain/tim-kiem?q=").parseHtml() val doc = webClient.httpGet("https://$domain/tim-kiem?q=").parseHtml()
val list = doc.getElementsByAttribute("data-genre-id") val list = doc.getElementsByAttribute("data-genre-id")

@ -53,6 +53,11 @@ internal class ManhwasMen(context: MangaLoaderContext) :
else -> { else -> {
if (filter.tags.isNotEmpty() && filter.states.isNotEmpty()) {
throw IllegalArgumentException("The source supports one filter at a time")
}
filter.tags.oneOrThrowIfMany()?.let { filter.tags.oneOrThrowIfMany()?.let {
append("&genero=") append("&genero=")
append(it.key) append(it.key)

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.parsers.site.en package org.koitharu.kotatsu.parsers.site.en
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.SinglePageMangaParser import org.koitharu.kotatsu.parsers.SinglePageMangaParser
@ -9,6 +10,7 @@ import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@Broken
@MangaSourceParser("PO2SCANS", "Po2Scans", "en") @MangaSourceParser("PO2SCANS", "Po2Scans", "en")
internal class Po2Scans(context: MangaLoaderContext) : SinglePageMangaParser(context, MangaParserSource.PO2SCANS) { internal class Po2Scans(context: MangaLoaderContext) : SinglePageMangaParser(context, MangaParserSource.PO2SCANS) {

@ -17,48 +17,64 @@ import java.util.*
internal class Pururin(context: MangaLoaderContext) : internal class Pururin(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.PURURIN, pageSize = 20) { PagedMangaParser(context, MangaParserSource.PURURIN, pageSize = 20) {
override val configKeyDomain = ConfigKey.Domain("pururin.to")
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override val availableSortOrders: Set<SortOrder> = override val availableSortOrders: Set<SortOrder> =
EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.RATING, SortOrder.ALPHABETICAL) EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.RATING, SortOrder.ALPHABETICAL)
override val configKeyDomain = ConfigKey.Domain("pururin.to")
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isSearchSupported = true, isSearchSupported = true,
isSearchWithFiltersSupported = true,
isMultipleTagsSupported = true,
isTagsExclusionSupported = true,
) )
override suspend fun getFilterOptions() = MangaListFilterOptions( override suspend fun getFilterOptions() = MangaListFilterOptions(
availableTags = fetchAvailableTags(), availableTags = fetchAvailableTags(),
) )
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> { override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = buildString { val url = buildString {
append("https://") append("https://")
append(domain) append(domain)
when { append("/search?tag_condition=contains&page=")
!filter.query.isNullOrEmpty() -> {
append("/search?q=")
append(filter.query.urlEncoded())
append("&page=")
append(page.toString()) append(page.toString())
filter.query?.let {
append("&q=")
append(filter.query.urlEncoded())
} }
else -> { if (filter.tags.isNotEmpty()) {
append("/browse") append("&included_tags=[")
filter.tags.joinToString(separator = ",") {
append("{\"id\":")
append(it.key)
append(",\"name\":\"")
append(it.title.replace(" ", "+"))
append("[Content]\"}")
}
append("]")
}
filter.tags.oneOrThrowIfMany()?.let { if (filter.tagsExclude.isNotEmpty()) {
append("/tags/content/") append("&excluded_tags=[")
filter.tagsExclude.joinToString(separator = ",") {
append("{\"id\":")
append(it.key) append(it.key)
append("/") append(",\"name\":\"")
append(it.title.replace(" ", "+"))
append("[Content]\"}")
}
append("]")
} }
append("?page=")
append(page)
append("&sort=") append("&sort=")
when (order) { when (order) {
@ -69,8 +85,6 @@ internal class Pururin(context: MangaLoaderContext) :
else -> append("") else -> append("")
} }
} }
}
}
val doc = webClient.httpGet(url).parseHtml() val doc = webClient.httpGet(url).parseHtml()
return doc.select(".row-gallery a.card-gallery").map { a -> return doc.select(".row-gallery a.card-gallery").map { a ->
val href = a.attrAsRelativeUrl("href") val href = a.attrAsRelativeUrl("href")
@ -109,7 +123,7 @@ internal class Pururin(context: MangaLoaderContext) :
val href = it.attr("href").substringAfterLast("content/").substringBeforeLast('/') val href = it.attr("href").substringAfterLast("content/").substringBeforeLast('/')
MangaTag( MangaTag(
key = href, key = href,
title = it.text(), title = it.text().substringBefore(" /"),
source = source, source = source,
) )
} }

@ -35,6 +35,9 @@ internal class VyManga(context: MangaLoaderContext) :
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isSearchSupported = true, isSearchSupported = true,
isSearchWithFiltersSupported = true,
isMultipleTagsSupported = true,
isTagsExclusionSupported = true,
) )
override suspend fun getFilterOptions() = MangaListFilterOptions( override suspend fun getFilterOptions() = MangaListFilterOptions(
@ -46,37 +49,19 @@ internal class VyManga(context: MangaLoaderContext) :
val url = buildString { val url = buildString {
append("https://") append("https://")
append(domain) append(domain)
when {
!filter.query.isNullOrEmpty() -> {
append("/search?search_po=0&q=") append("/search?search_po=0&q=")
append(filter.query.urlEncoded())
append("&author_po=0&author=&completed=2&sort=updated_at&sort_type=desc&page=")
append(page)
}
else -> {
if (filter.tags.isEmpty()) { filter.query?.let {
append(filter.query.urlEncoded())
append("/search?search_po=0&q=&author_po=0&author=&completed=")
filter.states.oneOrThrowIfMany()?.let {
append(
when (it) {
MangaState.ONGOING -> "0"
MangaState.FINISHED -> "1"
else -> "2"
},
)
} }
} else { append("&author_po=0&author=")
append("/genre/") // filter.author?.let {
filter.tags.oneOrThrowIfMany()?.let { // append(filter.author.urlEncoded())
append(it.key) // }
}
append("?status=") append("&completed=")
filter.states.oneOrThrowIfMany()?.let { filter.states.oneOrThrowIfMany()?.let {
append( append(
when (it) { when (it) {
@ -86,7 +71,6 @@ internal class VyManga(context: MangaLoaderContext) :
}, },
) )
} }
}
append("&sort=") append("&sort=")
when (order) { when (order) {
@ -98,13 +82,21 @@ internal class VyManga(context: MangaLoaderContext) :
SortOrder.NEWEST_ASC -> append("created_at&sort_type=asc") SortOrder.NEWEST_ASC -> append("created_at&sort_type=asc")
SortOrder.UPDATED -> append("updated_at&sort_type=desc") SortOrder.UPDATED -> append("updated_at&sort_type=desc")
SortOrder.UPDATED_ASC -> append("updated_at&sort_type=asc") SortOrder.UPDATED_ASC -> append("updated_at&sort_type=asc")
else -> append("Updated") else -> append("updated_at&sort_type=desc")
} }
append("&page=") filter.tags.forEach { tag ->
append(page) append("&genre[]=")
append(tag.key)
} }
filter.tagsExclude.forEach { tagsExclude ->
append("&exclude_genre[]=")
append(tagsExclude.key)
} }
append("&page=")
append(page.toString())
} }
val doc = webClient.httpGet(url).parseHtml() val doc = webClient.httpGet(url).parseHtml()
return doc.select(".comic-item").map { div -> return doc.select(".comic-item").map { div ->
@ -127,11 +119,11 @@ internal class VyManga(context: MangaLoaderContext) :
} }
private suspend fun fetchAvailableTags(): Set<MangaTag> { private suspend fun fetchAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain/").parseHtml() val doc = webClient.httpGet("https://$domain/search").parseHtml()
return doc.select("div.dropdown-menu.custom-menu ul li a[href*=genre]").mapNotNullToSet { return doc.select("#advance-search .check-genre .d-flex").mapNotNullToSet {
MangaTag( MangaTag(
key = it.attr("href").substringAfterLast('/'), key = it.selectFirstOrThrow(".checkbox-genre").attr("data-value"),
title = it.text(), title = it.selectFirstOrThrow("label").text(),
source = source, source = source,
) )
} }

Loading…
Cancel
Save