Artist filter support

Koitharu 1 year ago
parent 9a1d37166a
commit 522b3d53cc
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -15,6 +15,7 @@ public data class MangaListFilter(
@JvmField val year: Int = YEAR_UNKNOWN, @JvmField val year: Int = YEAR_UNKNOWN,
@JvmField val yearFrom: Int = YEAR_UNKNOWN, @JvmField val yearFrom: Int = YEAR_UNKNOWN,
@JvmField val yearTo: Int = YEAR_UNKNOWN, @JvmField val yearTo: Int = YEAR_UNKNOWN,
@JvmField val author: String? = null,
) { ) {
private fun isNonSearchOptionsEmpty(): Boolean = tags.isEmpty() && private fun isNonSearchOptionsEmpty(): Boolean = tags.isEmpty() &&
@ -27,7 +28,8 @@ public data class MangaListFilter(
yearFrom == YEAR_UNKNOWN && yearFrom == YEAR_UNKNOWN &&
yearTo == YEAR_UNKNOWN && yearTo == YEAR_UNKNOWN &&
types.isEmpty() && types.isEmpty() &&
demographics.isEmpty() demographics.isEmpty() &&
author.isNullOrEmpty()
public fun isEmpty(): Boolean = isNonSearchOptionsEmpty() && query.isNullOrEmpty() public fun isEmpty(): Boolean = isNonSearchOptionsEmpty() && query.isNullOrEmpty()

@ -47,4 +47,10 @@ public data class MangaListFilterCapabilities @InternalParsersApi constructor(
* @see [MangaListFilterOptions.availableLocales] * @see [MangaListFilterOptions.availableLocales]
*/ */
val isOriginalLocaleSupported: Boolean = false, val isOriginalLocaleSupported: Boolean = false,
/**
* Whether parser supports searching by author name
* @see [MangaListFilter.author]
*/
val isAuthorSearchSupported: Boolean = false,
) )

@ -19,6 +19,7 @@ import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.TooManyRequestExceptions import org.koitharu.kotatsu.parsers.exception.TooManyRequestExceptions
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.Collections.emptyList import java.util.Collections.emptyList
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -54,6 +55,7 @@ internal class ExHentaiParser(
isTagsExclusionSupported = true, isTagsExclusionSupported = true,
isSearchSupported = true, isSearchSupported = true,
isSearchWithFiltersSupported = true, isSearchWithFiltersSupported = true,
isAuthorSearchSupported = true,
) )
override val isAuthorized: Boolean override val isAuthorized: Boolean
@ -172,8 +174,8 @@ internal class ExHentaiParser(
url = href, url = href,
publicUrl = a.absUrl("href"), publicUrl = a.absUrl("href"),
rating = td2.selectFirst("div.ir")?.parseRating() ?: RATING_UNKNOWN, rating = td2.selectFirst("div.ir")?.parseRating() ?: RATING_UNKNOWN,
isNsfw = true, contentRating = ContentRating.ADULT,
coverUrl = td1.selectFirst("img")?.absUrl("src").orEmpty(), coverUrl = td1.selectFirst("img")?.attrAsAbsoluteUrlOrNull("src"),
tags = tagsDiv.parseTags(), tags = tagsDiv.parseTags(),
state = null, state = null,
author = tagsDiv.getElementsContainingOwnText("artist:").first() author = tagsDiv.getElementsContainingOwnText("artist:").first()
@ -190,9 +192,18 @@ internal class ExHentaiParser(
val title = root.getElementById("gd2") val title = root.getElementById("gd2")
val tagList = root.getElementById("taglist") val tagList = root.getElementById("taglist")
val tabs = doc.body().selectFirst("table.ptt")?.selectFirst("tr") val tabs = doc.body().selectFirst("table.ptt")?.selectFirst("tr")
val lang = root.getElementById("gd3") val gd3 = root.getElementById("gd3")
val lang = gd3
?.selectFirst("tr:contains(Language)") ?.selectFirst("tr:contains(Language)")
?.selectFirst(".gdt2")?.ownTextOrNull() ?.selectFirst(".gdt2")?.ownTextOrNull()
val uploadDate = gd3
?.selectFirst("tr:contains(Posted)")
?.selectFirst(".gdt2")?.ownTextOrNull()
.let { SimpleDateFormat("yyyy-MM-dd HH:mm", sourceLocale).tryParse(it) }
val uploader = gd3
?.getElementsByAttributeValueContaining("href", "/uploader/")
?.firstOrNull()
?.ownTextOrNull()
val tags = tagList?.parseTags().orEmpty() val tags = tagList?.parseTags().orEmpty()
return manga.copy( return manga.copy(
@ -223,9 +234,9 @@ internal class ExHentaiParser(
number = i.toFloat(), number = i.toFloat(),
volume = 0, volume = 0,
url = url, url = url,
uploadDate = 0L, uploadDate = uploadDate,
source = source, source = source,
scanlator = null, scanlator = uploader,
branch = lang, branch = lang,
) )
} }
@ -415,6 +426,11 @@ internal class ExHentaiParser(
joiner.append(lc.toLanguagePath()) joiner.append(lc.toLanguagePath())
joiner.append("\"$") joiner.append("\"$")
} }
if (!author.isNullOrEmpty()) {
joiner.add("artist:\"")
joiner.append(author)
joiner.append("\"$")
}
return joiner.complete().nullIfEmpty() return joiner.complete().nullIfEmpty()
} }

@ -103,10 +103,11 @@ internal abstract class GroupleParser(
} else { } else {
advancedSearch(offset, order, filter).parseHtml() advancedSearch(offset, order, filter).parseHtml()
} }
val tiles = val tiles = root.selectFirst("div.tiles.row")
root.selectFirst("div.tiles.row") ?: if (root.select(".alert").any { it.ownText() == NOTHING_FOUND }) { if (tiles == null) {
if (!root.getElementsContainingOwnText(NOTHING_FOUND).isNullOrEmpty()) {
return emptyList() return emptyList()
} else { }
root.parseFailed("No tiles found") root.parseFailed("No tiles found")
} }
return tiles.select("div.tile").mapNotNull(::parseManga) return tiles.select("div.tile").mapNotNull(::parseManga)

Loading…
Cancel
Save