VyvyManga: add filters & update domain (#2150)

* Vyvymanga: implemented new filters for advanced search

# Conflicts:
#	src/en/vyvymanga/src/eu/kanade/tachiyomi/extension/en/vyvymanga/VyvyManga.kt

* Move filters to separated file

* Fetch online genres

* Update domain, bump version

* revert unnecessary change

---------

Co-authored-by: Ota Takushima <ota.takushima@tum.de>
This commit is contained in:
Cuong M. Tran 2024-03-29 14:01:07 +07:00 committed by GitHub
parent 324c4078a0
commit d41a1237af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 142 additions and 4 deletions

View File

@ -1,7 +1,7 @@
ext {
extName = 'VyvyManga'
extClass = '.VyvyManga'
extVersionCode = 4
extVersionCode = 5
isNsfw = true
}

View File

@ -18,7 +18,7 @@ import java.util.Locale
class VyvyManga : ParsedHttpSource() {
override val name = "VyvyManga"
override val baseUrl = "https://vyvymanga.net"
override val baseUrl = "https://vymanga.net"
override val lang = "en"
@ -44,7 +44,23 @@ class VyvyManga : ParsedHttpSource() {
val url = "$baseUrl/search".toHttpUrl().newBuilder()
.addQueryParameter("q", query)
.addQueryParameter("page", page.toString())
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
is SearchType -> url.addQueryParameter("search_po", filter.selected)
is SearchDescription -> if (filter.state) url.addQueryParameter("check_search_desc", "1")
is AuthorSearchType -> url.addQueryParameter("author_po", filter.selected)
is AuthorFilter -> url.addQueryParameter("author", filter.state)
is StatusFilter -> url.addQueryParameter("completed", filter.selected)
is SortFilter -> url.addQueryParameter("sort", filter.selected)
is SortType -> url.addQueryParameter("sort_type", filter.selected)
is GenreFilter -> {
filter.state.forEach {
if (!it.isIgnored()) url.addQueryParameter(if (it.isIncluded()) "genre[]" else "exclude_genre[]", it.id)
}
}
else -> {}
}
}
return GET(url.build(), headers)
}
@ -68,7 +84,7 @@ class VyvyManga : ParsedHttpSource() {
override fun latestUpdatesFromElement(element: Element): SManga =
searchMangaFromElement(element)
override fun latestUpdatesNextPageSelector(): String? =
override fun latestUpdatesNextPageSelector() =
searchMangaNextPageSelector()
// Details
@ -143,4 +159,18 @@ class VyvyManga : ParsedHttpSource() {
else -> 0
}
}
override fun getFilterList(): FilterList {
launchIO { fetchGenres(baseUrl, headers, client) }
return FilterList(
SearchType(),
SearchDescription(),
AuthorSearchType(),
AuthorFilter(),
StatusFilter(),
SortFilter(),
SortType(),
GenreFilter(),
)
}
}

View File

@ -0,0 +1,108 @@
package eu.kanade.tachiyomi.extension.en.vyvymanga
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.jsoup.nodes.Document
abstract class SelectFilter(displayName: String, private val options: Array<Pair<String, String>>) :
Filter.Select<String>(
displayName,
options.map { it.first }.toTypedArray(),
) {
open val selected get() = options[state].second.takeUnless { it.isEmpty() }
}
class SearchType : SelectFilter(
"Title should contain/begin/end with typed text",
arrayOf(
Pair("Contain", "0"),
Pair("Begin", "1"),
Pair("End", "2"),
),
)
class SearchDescription : Filter.CheckBox("Search In Description")
class AuthorSearchType : SelectFilter(
"Author should contain/begin/end with typed text",
arrayOf(
Pair("Contain", "0"),
Pair("Begin", "1"),
Pair("End", "2"),
),
)
class AuthorFilter : Filter.Text("Author")
class StatusFilter : SelectFilter(
"Status",
arrayOf(
Pair("All", "2"),
Pair("Ongoing", "0"),
Pair("Completed", "1"),
),
)
class SortFilter : SelectFilter(
"Sort by",
arrayOf(
Pair("Viewed", "viewed"),
Pair("Scored", "scored"),
Pair("Newest", "created_at"),
Pair("Latest Update", "updated_at"),
),
)
class SortType : SelectFilter(
"Sort order",
arrayOf(
Pair("Descending", "desc"),
Pair("Ascending", "asc"),
),
)
class Genre(name: String, val id: String) : Filter.TriState(name)
class GenreFilter : Filter.Group<Genre>("Genre", genrePairs.map { Genre(it.name, it.id) })
private var genrePairs: List<Genre> = emptyList()
private val scope = CoroutineScope(Dispatchers.IO)
fun launchIO(block: () -> Unit) = scope.launch { block() }
private var fetchGenresAttempts: Int = 0
fun fetchGenres(baseUrl: String, headers: okhttp3.Headers, client: okhttp3.OkHttpClient) {
if (fetchGenresAttempts < 3 && genrePairs.isEmpty()) {
try {
genrePairs =
client.newCall(genresRequest(baseUrl, headers)).execute()
.asJsoup()
.let(::parseGenres)
} catch (_: Exception) {
} finally {
fetchGenresAttempts++
}
}
}
private fun genresRequest(baseUrl: String, headers: okhttp3.Headers) = GET("$baseUrl/search", headers)
private const val genresSelector = ".check-genre div div:has(.checkbox-genre)"
private fun parseGenres(document: Document): List<Genre> {
val items = document.select(genresSelector)
return buildList(items.size) {
items.mapTo(this) {
Genre(
it.select("label").text(),
it.select(".checkbox-genre").attr("data-value"),
)
}
}
}