Implement search and filters (#8706)

This commit is contained in:
Arraiment 2021-08-22 01:32:34 +08:00 committed by GitHub
parent 4ba9118ce3
commit 40ea248097
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 22 deletions

View File

@ -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'
} }

View File

@ -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