[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) :
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(
SortOrder.RATING,
SortOrder.UPDATED,
SortOrder.NEWEST,
SortOrder.POPULARITY,
SortOrder.ALPHABETICAL_DESC,
SortOrder.ALPHABETICAL,
)
override val configKeyDomain = ConfigKey.Domain("asuracomic.net")
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isMultipleTagsSupported = true,
isSearchSupported = true,
isSearchWithFiltersSupported = true,
)
override suspend fun getFilterOptions() = MangaListFilterOptions(
availableTags = getOrCreateTagMap().values.toSet(),
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> {
val url = buildString {
append("https://")
@ -50,14 +56,11 @@ internal class AsuraScansParser(context: MangaLoaderContext) :
append("/series?page=")
append(page)
when {
!filter.query.isNullOrEmpty() -> {
filter.query?.let {
append("&name=")
append(filter.query.urlEncoded())
}
else -> {
if (filter.tags.isNotEmpty()) {
append("&genres=")
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) {
SortOrder.RATING -> append("rating")
SortOrder.UPDATED -> append("update")
SortOrder.NEWEST -> append("latest")
SortOrder.POPULARITY -> append("bookmarks")
SortOrder.ALPHABETICAL_DESC -> append("desc")
SortOrder.ALPHABETICAL -> append("asc")
else -> append("update")
}
}
}
}
val doc = webClient.httpGet(url).parseHtml()
return doc.select("div.grid > a[href]").map { a ->
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,
tags = emptySet(),
author = null,
state = when (a.selectLastOrThrow("span.status").text()) {
state = when (a.selectLast("span.status")?.text().orEmpty()) {
"Ongoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED
"Hiatus" -> MangaState.PAUSED

@ -18,10 +18,7 @@ internal class CloneMangaParser(context: MangaLoaderContext) :
override val configKeyDomain = ConfigKey.Domain("manga.clone-army.org")
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isSearchSupported = true,
)
override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities()
override suspend fun getFilterOptions() = MangaListFilterOptions()
@ -31,9 +28,6 @@ internal class CloneMangaParser(context: MangaLoaderContext) :
}
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 mangas = doc.getElementsByClass("comicPreviewContainer")
return mangas.mapNotNull { item ->

@ -14,9 +14,6 @@ import java.util.*
@MangaSourceParser("COMICEXTRA", "ComicExtra", "en")
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 userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)
@ -26,6 +23,9 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
isSearchSupported = true,
)
override val availableSortOrders: Set<SortOrder> =
EnumSet.of(SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.NEWEST)
override suspend fun getFilterOptions() = MangaListFilterOptions(
availableTags = fetchAvailableTags(),
availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED),
@ -52,6 +52,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
}
else -> {
if (filter.tags.isNotEmpty() && filter.states.isEmpty()) {
filter.tags.oneOrThrowIfMany()?.let {
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)
} else {
when (order) {

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

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

@ -22,6 +22,8 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isSearchSupported = true,
isMultipleTagsSupported = true,
isTagsExclusionSupported = true,
)
override suspend fun getFilterOptions() = MangaListFilterOptions(
@ -51,20 +53,24 @@ internal class MangaGeko(context: MangaLoaderContext) : PagedMangaParser(context
append("/browse-comics/?results=")
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=")
when (order) {
SortOrder.POPULARITY -> append("views")
SortOrder.UPDATED -> append("Updated")
SortOrder.NEWEST -> append("New")
// SortOrder.RANDOM -> append("Random")
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> {
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(
key = label.selectFirstOrThrow("input").attr("value"),
title = label.text(),

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

@ -29,13 +29,12 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
SortOrder.RATING,
)
private val tagsMap = SuspendLazy(::parseTags)
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isMultipleTagsSupported = true,
isTagsExclusionSupported = true,
isSearchSupported = true,
isSearchWithFiltersSupported = true,
)
override suspend fun getFilterOptions() = MangaListFilterOptions(
@ -63,14 +62,11 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
append("/tim-kiem?page=")
append(page.toString())
when {
!filter.query.isNullOrEmpty() -> {
filter.query?.let {
append("&q=")
append(filter.query.urlEncoded())
}
else -> {
append("&accept_genres=")
if (filter.tags.isNotEmpty()) {
append(
@ -94,7 +90,7 @@ internal class Manhwa18Parser(context: MangaLoaderContext) :
SortOrder.UPDATED -> "update"
SortOrder.NEWEST -> "new"
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()
@ -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 {
timeStr ?: return 0
val timeWords = timeStr.split(' ')
if (timeWords.size != 3) return 0
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> {
val doc = webClient.httpGet("https://$domain/tim-kiem?q=").parseHtml()
val list = doc.getElementsByAttribute("data-genre-id")

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

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

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

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

Loading…
Cancel
Save