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:
parent
02f70b000d
commit
9637963a6c
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'VyvyManga'
|
extName = 'VyvyManga'
|
||||||
extClass = '.VyvyManga'
|
extClass = '.VyvyManga'
|
||||||
extVersionCode = 4
|
extVersionCode = 5
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import java.util.Locale
|
||||||
class VyvyManga : ParsedHttpSource() {
|
class VyvyManga : ParsedHttpSource() {
|
||||||
override val name = "VyvyManga"
|
override val name = "VyvyManga"
|
||||||
|
|
||||||
override val baseUrl = "https://vyvymanga.net"
|
override val baseUrl = "https://vymanga.net"
|
||||||
|
|
||||||
override val lang = "en"
|
override val lang = "en"
|
||||||
|
|
||||||
|
@ -44,7 +44,23 @@ class VyvyManga : ParsedHttpSource() {
|
||||||
val url = "$baseUrl/search".toHttpUrl().newBuilder()
|
val url = "$baseUrl/search".toHttpUrl().newBuilder()
|
||||||
.addQueryParameter("q", query)
|
.addQueryParameter("q", query)
|
||||||
.addQueryParameter("page", page.toString())
|
.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)
|
return GET(url.build(), headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +84,7 @@ class VyvyManga : ParsedHttpSource() {
|
||||||
override fun latestUpdatesFromElement(element: Element): SManga =
|
override fun latestUpdatesFromElement(element: Element): SManga =
|
||||||
searchMangaFromElement(element)
|
searchMangaFromElement(element)
|
||||||
|
|
||||||
override fun latestUpdatesNextPageSelector(): String? =
|
override fun latestUpdatesNextPageSelector() =
|
||||||
searchMangaNextPageSelector()
|
searchMangaNextPageSelector()
|
||||||
|
|
||||||
// Details
|
// Details
|
||||||
|
@ -143,4 +159,18 @@ class VyvyManga : ParsedHttpSource() {
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getFilterList(): FilterList {
|
||||||
|
launchIO { fetchGenres(baseUrl, headers, client) }
|
||||||
|
return FilterList(
|
||||||
|
SearchType(),
|
||||||
|
SearchDescription(),
|
||||||
|
AuthorSearchType(),
|
||||||
|
AuthorFilter(),
|
||||||
|
StatusFilter(),
|
||||||
|
SortFilter(),
|
||||||
|
SortType(),
|
||||||
|
GenreFilter(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue