MiMiHentai: Advanced Search (#9773)

* Advanced Search

* Add if search

* Update src/vi/mimihentai/src/eu/kanade/tachiyomi/extension/vi/mimihentai/MiMiHentai.kt

Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com>

* Fix build

* Apply suggestion

* Complete Advanced Search

* Revert "Complete Advanced Search"

This reverts commit 25a77c5d9be035f64fe5d9e418686830fb41cde8.

* Delete hs_err_pid25072.log

* Advanced Search

* suggestion

* Revert not apply suggestion because Inefficient

* Update MiMiHentai.kt

* Apply suggestion

---------

Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com>
This commit is contained in:
are-are-are 2025-07-25 23:03:26 +07:00 committed by Draff
parent 7d6df5d918
commit 99a153304d
Signed by: Draff
GPG Key ID: E8A89F3211677653
3 changed files with 69 additions and 10 deletions

View File

@ -1,7 +1,7 @@
ext { ext {
extName = "MiMiHentai" extName = "MiMiHentai"
extClass = ".MiMiHentai" extClass = ".MiMiHentai"
extVersionCode = 1 extVersionCode = 2
isNsfw = true isNsfw = true
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -62,3 +62,9 @@ private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS", Locale
class PageListDto( class PageListDto(
val pages: List<String>, val pages: List<String>,
) )
@Serializable
class Genres(
val id: Long,
val name: String,
)

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.vi.mimihentai
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -11,6 +12,9 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import keiyoushi.utils.parseAs import keiyoushi.utils.parseAs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -38,7 +42,7 @@ class MiMiHentai : HttpSource() {
override fun latestUpdatesRequest(page: Int): Request = GET( override fun latestUpdatesRequest(page: Int): Request = GET(
apiUrl.toHttpUrl().newBuilder().apply { apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("tatcatruyen") addPathSegments("tatcatruyen")
addQueryParameter("page", page.toString()) addQueryParameter("page", (page - 1).toString())
addQueryParameter("sort", "updated_at") addQueryParameter("sort", "updated_at")
}.build(), }.build(),
headers, headers,
@ -106,7 +110,7 @@ class MiMiHentai : HttpSource() {
override fun popularMangaRequest(page: Int): Request = GET( override fun popularMangaRequest(page: Int): Request = GET(
apiUrl.toHttpUrl().newBuilder().apply { apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("tatcatruyen") addPathSegments("tatcatruyen")
addQueryParameter("page", page.toString()) addQueryParameter("page", (page - 1).toString())
addQueryParameter("sort", "views") addQueryParameter("sort", "views")
}.build(), }.build(),
headers, headers,
@ -139,10 +143,12 @@ class MiMiHentai : HttpSource() {
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = apiUrl.toHttpUrl().newBuilder().apply { val url = apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("search") addPathSegments("advance-search")
addQueryParameter("page", page.toString()) addQueryParameter("author", "")
addQueryParameter("character", "")
addQueryParameter("parody", "")
addQueryParameter("page", (page - 1).toString())
addQueryParameter("name", query) addQueryParameter("name", query)
(if (filters.isEmpty()) getFilterList() else filters).forEach { filters -> (if (filters.isEmpty()) getFilterList() else filters).forEach { filters ->
when (filters) { when (filters) {
is SortByList -> is SortByList ->
@ -150,7 +156,10 @@ class MiMiHentai : HttpSource() {
val sort = getSortByList()[filters.state] val sort = getSortByList()[filters.state]
addQueryParameter("sort", sort.id) addQueryParameter("sort", sort.id)
} }
is GenreList -> {
filters.state.forEach { genre -> if (genre.state) addQueryParameter("genre", genre.id) }
}
is TextField -> setQueryParameter(filters.key, filters.state)
else -> {} else -> {}
} }
} }
@ -158,6 +167,38 @@ class MiMiHentai : HttpSource() {
return GET(url, headers) return GET(url, headers)
} }
private fun genresRequest(): Request = GET("$apiUrl/genres", headers)
private fun parseGenres(response: Response): List<Pair<Long, String>> {
return response.parseAs<List<Genres>>().map { Pair(it.id, it.name) }
}
private var fetchGenresAttempts: Int = 0
private fun fetchGenres() {
if (fetchGenresAttempts >= 3 || genreList.isEmpty()) {
launchIO {
try {
client.newCall(genresRequest()).await()
.use { parseGenres(it) }
.takeIf { it.isNotEmpty() }
?.also { genreList = it }
} catch (_: Exception) {
} finally {
fetchGenresAttempts++
}
}
}
}
private fun launchIO(block: suspend () -> Unit) = GlobalScope.launch(Dispatchers.IO) { block() }
private var genreList: List<Pair<Long, String>> = emptyList()
private class GenreList(name: String, pairs: List<Pair<Long, String>>) : GenresFilter(name, pairs)
private open class GenresFilter(title: String, pairs: List<Pair<Long, String>>) :
Filter.Group<GenreCheckBox>(title, pairs.map { GenreCheckBox(it.second, it.first.toString()) })
class GenreCheckBox(name: String, val id: String = name) : Filter.CheckBox(name)
private class SortByList(sort: Array<SortBy>) : Filter.Select<SortBy>("Sắp xếp", sort) private class SortByList(sort: Array<SortBy>) : Filter.Select<SortBy>("Sắp xếp", sort)
private class SortBy(name: String, val id: String) : Filter.CheckBox(name) { private class SortBy(name: String, val id: String) : Filter.CheckBox(name) {
override fun toString(): String { override fun toString(): String {
@ -165,9 +206,21 @@ class MiMiHentai : HttpSource() {
} }
} }
override fun getFilterList() = FilterList( private class TextField(name: String, val key: String) : Filter.Text(name)
override fun getFilterList(): FilterList {
fetchGenres()
return FilterList(
SortByList(getSortByList()), SortByList(getSortByList()),
TextField("Tác giả", "author"),
TextField("Parody", "parody"),
TextField("Nhân vật", "character"),
if (genreList.isEmpty()) {
Filter.Header("Nhấn 'Làm mới' để thử tải thể loại")
} else {
GenreList("Thể loại", genreList)
},
) )
}
private fun getSortByList() = arrayOf( private fun getSortByList() = arrayOf(
SortBy("Mới", "updated_at"), SortBy("Mới", "updated_at"),