Implement search and filters (#8706)
This commit is contained in:
parent
4ba9118ce3
commit
40ea248097
@ -1,11 +1,12 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlinx-serialization'
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
extName = 'Comic Fx'
|
extName = 'Comic Fx'
|
||||||
pkgNameSuffix = 'id.comicfx'
|
pkgNameSuffix = 'id.comicfx'
|
||||||
extClass = '.ComicFx'
|
extClass = '.ComicFx'
|
||||||
extVersionCode = 2
|
extVersionCode = 3
|
||||||
libVersion = '1.2'
|
libVersion = '1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,27 @@
|
|||||||
package eu.kanade.tachiyomi.extension.id.comicfx
|
package eu.kanade.tachiyomi.extension.id.comicfx
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
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
|
||||||
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
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.ParsedHttpSource
|
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.jsonArray
|
||||||
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import uy.kohesive.injekt.api.get
|
import rx.Observable
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.text.ParseException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@ -62,14 +71,54 @@ class ComicFx : ParsedHttpSource() {
|
|||||||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
|
|
||||||
|
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||||
|
// Text search cannot use filters
|
||||||
|
if (query.isNotEmpty()) {
|
||||||
|
return client.newCall(GET("$baseUrl/search?query=$query"))
|
||||||
|
.asObservableSuccess()
|
||||||
|
.map { response ->
|
||||||
|
parseSearchApiResponse(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.fetchSearchManga(page, query, filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
private fun parseSearchApiResponse(response: Response): MangasPage {
|
||||||
|
val results = json.parseToJsonElement(response.body!!.string()).jsonObject["suggestions"]!!.jsonArray
|
||||||
|
val manga = results.map {
|
||||||
|
SManga.create().apply {
|
||||||
|
title = it.jsonObject["value"]!!.jsonPrimitive.content
|
||||||
|
url = "/komik/${it.jsonObject["data"]!!.jsonPrimitive.content}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MangasPage(manga, false)
|
||||||
|
}
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
val filters = if (filters.isEmpty()) getFilterList() else filters
|
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||||
val genre = filters.findInstance<GenreList>()?.toUriPart()
|
|
||||||
val order = filters.findInstance<OrderByFilter>()?.toUriPart()
|
|
||||||
|
|
||||||
// todo search
|
val url = "$baseUrl/filterList".toHttpUrlOrNull()!!.newBuilder()
|
||||||
|
|
||||||
return GET("$baseUrl/filterList?page=$page&cstatus=&ctype=&cat=$genre&alpha=&$order&author=&artist=&tag=") // filter
|
for (filter in filterList) {
|
||||||
|
when (filter) {
|
||||||
|
is GenreFilter -> url.addQueryParameter("cat", filter.toUriPart())
|
||||||
|
is SortFilter -> {
|
||||||
|
url.addQueryParameter("sortBy", filter.toUriPart())
|
||||||
|
url.addQueryParameter("asc", filter.state!!.ascending.toString())
|
||||||
|
}
|
||||||
|
is StatusFilter -> url.addQueryParameter("cstatus", filter.toUriPart())
|
||||||
|
is TypeFilter -> url.addQueryParameter("ctype", filter.toUriPart())
|
||||||
|
is AuthorFilter -> url.addQueryParameter("author", filter.state)
|
||||||
|
is ArtistFilter -> url.addQueryParameter("artist", filter.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
url.addQueryParameter("page", page.toString())
|
||||||
|
// Unimplemented parameters: "alpha" (For filtering by alphabet) and "tag" (idk)
|
||||||
|
return GET(url.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchMangaSelector() = popularMangaSelector()
|
override fun searchMangaSelector() = popularMangaSelector()
|
||||||
@ -105,18 +154,31 @@ class ComicFx : ParsedHttpSource() {
|
|||||||
val updateOn = document.select(".infokomik .infolengkap span:contains(update) b").text()
|
val updateOn = document.select(".infokomik .infolengkap span:contains(update) b").text()
|
||||||
val date = document.select(".infokomik .infolengkap span:contains(update)").text().substringAfter(updateOn)
|
val date = document.select(".infokomik .infolengkap span:contains(update)").text().substringAfter(updateOn)
|
||||||
val checkChapter = document.select(chapterListSelector()).firstOrNull()
|
val checkChapter = document.select(chapterListSelector()).firstOrNull()
|
||||||
if (date != "" && checkChapter != null) chapters[0].date_upload = parseDate(date)
|
if (date != "" && checkChapter != null && chapters[0].date_upload == 0L) {
|
||||||
|
chapters[0].date_upload = SimpleDateFormat("dd mmm yyyy", Locale.ENGLISH).parse(date)?.time ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
return chapters
|
return chapters
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseDate(date: String): Long {
|
private fun parseDate(date: String): Long {
|
||||||
return SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH).parse(date)?.time ?: 0L
|
return when (date) {
|
||||||
|
"hari ini" -> System.currentTimeMillis()
|
||||||
|
"kemarin" -> System.currentTimeMillis() - (1000 * 60 * 60 * 24) // yesterday
|
||||||
|
else -> {
|
||||||
|
try {
|
||||||
|
SimpleDateFormat("dd-mm-yyyy", Locale.ENGLISH).parse(date)?.time ?: 0L
|
||||||
|
} catch (_: ParseException) {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterFromElement(element: Element) = SChapter.create().apply {
|
override fun chapterFromElement(element: Element) = SChapter.create().apply {
|
||||||
setUrlWithoutDomain(element.attr("href"))
|
setUrlWithoutDomain(element.attr("href"))
|
||||||
name = element.text()
|
name = element.selectFirst("span.chapternum").text()
|
||||||
|
date_upload = parseDate(element.selectFirst("span.chapterdate").text())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
@ -137,22 +199,29 @@ class ComicFx : ParsedHttpSource() {
|
|||||||
|
|
||||||
// filters
|
// filters
|
||||||
override fun getFilterList() = FilterList(
|
override fun getFilterList() = FilterList(
|
||||||
OrderByFilter(),
|
SortFilter(sortList),
|
||||||
GenreList()
|
GenreFilter(),
|
||||||
|
StatusFilter(),
|
||||||
|
TypeFilter(),
|
||||||
|
ArtistFilter("Artist"),
|
||||||
|
AuthorFilter("Author")
|
||||||
)
|
)
|
||||||
|
|
||||||
private class OrderByFilter : UriPartFilter(
|
private class ArtistFilter(name: String) : Filter.Text(name)
|
||||||
"Sort by",
|
private class AuthorFilter(name: String) : Filter.Text(name)
|
||||||
arrayOf(
|
|
||||||
Pair("sortBy=name&asc=true", "Default"),
|
private class SortFilter(val sortables: List<Pair<String, String>>) : Filter.Sort("Sort", sortables.map { it.second }.toTypedArray(), Selection(1, false)) {
|
||||||
Pair("sortBy=name&asc=true", "A-Z"),
|
fun toUriPart(): String {
|
||||||
Pair("sortBy=name&asc=false", "Z-A"),
|
return sortables[this.state!!.index].first
|
||||||
Pair("sortBy=views&asc=false", "Popular to Less"),
|
}
|
||||||
Pair("sortBy=views&asc=true", "Less to Popular")
|
}
|
||||||
)
|
|
||||||
|
private val sortList = listOf(
|
||||||
|
Pair("name", "Alphabetical"),
|
||||||
|
Pair("views", "Popular"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private class GenreList : UriPartFilter(
|
private class GenreFilter : UriPartFilter(
|
||||||
"Select Genre",
|
"Select Genre",
|
||||||
arrayOf(
|
arrayOf(
|
||||||
Pair("", "<select>"),
|
Pair("", "<select>"),
|
||||||
@ -190,6 +259,26 @@ class ComicFx : ParsedHttpSource() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private class StatusFilter : UriPartFilter(
|
||||||
|
"Status",
|
||||||
|
arrayOf(
|
||||||
|
Pair("", "All"),
|
||||||
|
Pair("1", "Ongoing"),
|
||||||
|
Pair("2", "Complete")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private class TypeFilter : UriPartFilter(
|
||||||
|
"Type",
|
||||||
|
arrayOf(
|
||||||
|
Pair("", "All"),
|
||||||
|
Pair("1", "Manhua (Chinese)"),
|
||||||
|
Pair("2", "Manhwa (Korean)"),
|
||||||
|
Pair("3", "Manga"),
|
||||||
|
Pair("4", "Oneshot"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||||
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray()) {
|
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray()) {
|
||||||
fun toUriPart() = vals[state].first
|
fun toUriPart() = vals[state].first
|
||||||
|
Loading…
x
Reference in New Issue
Block a user