HeanCms: Fix Search (#14843)

* Fix Search

* Fix Search

* Fix Search

* Apply requested changes

* Apply requested changes

* Apply requested changes

* Apply requested changes

* Apply requested changes
This commit is contained in:
Seew 2023-01-09 18:38:36 -05:00 committed by GitHub
parent 9c000859b9
commit c0a6c67cfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 30 deletions

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.multisrc.heancms
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
@ -41,14 +42,14 @@ abstract class HeanCms(
protected open val coverPath: String = "cover/" protected open val coverPath: String = "cover/"
private var seriesSlugMap: Map<String, String>? = null private var seriesSlugMap: Map<String, HeanCmsTitle>? = null
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Origin", baseUrl) .add("Origin", baseUrl)
.add("Referer", "$baseUrl/") .add("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
val payloadObj = HeanCmsSearchPayloadDto( val payloadObj = HeanCmsQuerySearchPayloadDto(
page = page, page = page,
order = "desc", order = "desc",
orderBy = "total_views", orderBy = "total_views",
@ -70,7 +71,7 @@ abstract class HeanCms(
val json = response.body?.string().orEmpty() val json = response.body?.string().orEmpty()
if (json.startsWith("{")) { if (json.startsWith("{")) {
val result = json.parseAs<HeanCmsSearchDto>() val result = json.parseAs<HeanCmsQuerySearchDto>()
val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) }
fetchAllTitles() fetchAllTitles()
@ -87,7 +88,7 @@ abstract class HeanCms(
} }
override fun latestUpdatesRequest(page: Int): Request { override fun latestUpdatesRequest(page: Int): Request {
val payloadObj = HeanCmsSearchPayloadDto( val payloadObj = HeanCmsQuerySearchPayloadDto(
page = page, page = page,
order = "desc", order = "desc",
orderBy = "latest", orderBy = "latest",
@ -110,7 +111,7 @@ abstract class HeanCms(
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val sortByFilter = filters.firstInstanceOrNull<SortByFilter>() val sortByFilter = filters.firstInstanceOrNull<SortByFilter>()
val payloadObj = HeanCmsSearchPayloadDto( val payloadObj = HeanCmsQuerySearchPayloadDto(
page = page, page = page,
order = if (sortByFilter?.state?.ascending == true) "asc" else "desc", order = if (sortByFilter?.state?.ascending == true) "asc" else "desc",
orderBy = sortByFilter?.selected ?: "total_views", orderBy = sortByFilter?.selected ?: "total_views",
@ -122,15 +123,25 @@ abstract class HeanCms(
.orEmpty() .orEmpty()
) )
val searchPayloadObj = HeanCmsSearchPayloadDto(
term = query
)
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE) val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
val searchPayload = json.encodeToString(searchPayloadObj).toRequestBody(JSON_MEDIA_TYPE)
val apiHeaders = headersBuilder() val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON) .add("Accept", ACCEPT_JSON)
.add("Content-Type", payload.contentType().toString()) .add("Content-Type", payload.contentType().toString())
.build() .build()
if (query.isNotBlank()) {
val apiUrl = "$apiUrl/series/search".toHttpUrl().newBuilder()
.toString()
return POST(apiUrl, apiHeaders, searchPayload)
}
val apiUrl = "$apiUrl/series/querysearch".toHttpUrl().newBuilder() val apiUrl = "$apiUrl/series/querysearch".toHttpUrl().newBuilder()
.addQueryParameter("q", query)
.toString() .toString()
return POST(apiUrl, apiHeaders, payload) return POST(apiUrl, apiHeaders, payload)
@ -138,33 +149,54 @@ abstract class HeanCms(
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
val json = response.body?.string().orEmpty() val json = response.body?.string().orEmpty()
val query = response.request.url.queryParameter("q").orEmpty()
if (response.request.url.pathSegments.last() == "search") {
val result = json.parseAs<List<HeanCmsSearchDto>>()
val mangaList = result
.filter { it.type == "Comic" }
.map {
createSearchResult(it.slug.replace(TIMESTAMP_REGEX, ""), it.title)
}
fetchAllTitles()
return MangasPage(mangaList, false)
}
if (json.startsWith("{")) { if (json.startsWith("{")) {
val result = json.parseAs<HeanCmsSearchDto>() val result = json.parseAs<HeanCmsQuerySearchDto>()
var mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) }
if (query.isNotBlank()) {
mangaList = mangaList.filter { it.title.contains(query, ignoreCase = true) }
}
fetchAllTitles() fetchAllTitles()
return MangasPage(mangaList, result.meta?.hasNextPage ?: false) return MangasPage(mangaList, result.meta?.hasNextPage ?: false)
} }
var mangaList = json.parseAs<List<HeanCmsSeriesDto>>() val mangaList = json.parseAs<List<HeanCmsSeriesDto>>()
.map { it.toSManga(apiUrl, coverPath) } .map { it.toSManga(apiUrl, coverPath) }
if (query.isNotBlank()) {
mangaList = mangaList.filter { it.title.contains(query, ignoreCase = true) }
}
fetchAllTitles() fetchAllTitles()
return MangasPage(mangaList, hasNextPage = false) return MangasPage(mangaList, hasNextPage = false)
} }
private fun createSearchResult(slug: String, title: String): SManga {
val theSeries = seriesSlugMap?.get(slug)
return SManga.create().apply {
url = "/series/$slug"
this.title = title
thumbnail_url = "$apiUrl/$coverPath${theSeries?.thumbnailUrl}"
this.status = when (theSeries?.status) {
"Ongoing" -> SManga.ONGOING
"Hiatus" -> SManga.ON_HIATUS
"Dropped" -> SManga.CANCELLED
"Completed", "Finished" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
}
}
// Workaround to allow "Open in browser" use the real URL. // Workaround to allow "Open in browser" use the real URL.
override fun fetchMangaDetails(manga: SManga): Observable<SManga> { override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(seriesDetailsRequest(manga)) return client.newCall(seriesDetailsRequest(manga))
@ -179,7 +211,7 @@ abstract class HeanCms(
.substringAfterLast("/") .substringAfterLast("/")
.replace(TIMESTAMP_REGEX, "") .replace(TIMESTAMP_REGEX, "")
val currentSlug = seriesSlugMap?.get(seriesSlug) ?: seriesSlug val currentSlug = seriesSlugMap?.get(seriesSlug)?.slug ?: seriesSlug
return GET("$baseUrl/series/$currentSlug", headers) return GET("$baseUrl/series/$currentSlug", headers)
} }
@ -191,7 +223,7 @@ abstract class HeanCms(
fetchAllTitles() fetchAllTitles()
val currentSlug = seriesSlugMap?.get(seriesSlug) ?: seriesSlug val currentSlug = seriesSlugMap?.get(seriesSlug)?.slug ?: seriesSlug
val apiHeaders = headersBuilder() val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON) .add("Accept", ACCEPT_JSON)
@ -271,14 +303,14 @@ abstract class HeanCms(
val result = runCatching { val result = runCatching {
var hasNextPage = true var hasNextPage = true
var page = 1 var page = 1
val tempMap = mutableMapOf<String, String>() val tempMap = mutableMapOf<String, HeanCmsTitle>()
while (hasNextPage) { while (hasNextPage) {
val response = client.newCall(allTitlesRequest(page)).execute() val response = client.newCall(allTitlesRequest(page)).execute()
val json = response.body?.string().orEmpty() val json = response.body?.string().orEmpty()
if (json.startsWith("{")) { if (json.startsWith("{")) {
val result = json.parseAs<HeanCmsSearchDto>() val result = json.parseAs<HeanCmsQuerySearchDto>()
tempMap.putAll(parseAllTitles(result.data)) tempMap.putAll(parseAllTitles(result.data))
hasNextPage = result.meta?.hasNextPage ?: false hasNextPage = result.meta?.hasNextPage ?: false
page++ page++
@ -296,7 +328,7 @@ abstract class HeanCms(
} }
protected open fun allTitlesRequest(page: Int): Request { protected open fun allTitlesRequest(page: Int): Request {
val payloadObj = HeanCmsSearchPayloadDto( val payloadObj = HeanCmsQuerySearchPayloadDto(
page = page, page = page,
order = "desc", order = "desc",
orderBy = "total_views", orderBy = "total_views",
@ -314,12 +346,18 @@ abstract class HeanCms(
return POST("$apiUrl/series/querysearch", apiHeaders, payload) return POST("$apiUrl/series/querysearch", apiHeaders, payload)
} }
protected open fun parseAllTitles(result: List<HeanCmsSeriesDto>): Map<String, String> { protected open fun parseAllTitles(result: List<HeanCmsSeriesDto>): Map<String, HeanCmsTitle> {
return result return result
.filter { it.type == "Comic" } .filter { it.type == "Comic" }
.associateBy( .associateBy(
keySelector = { it.slug.replace(TIMESTAMP_REGEX, "") }, keySelector = { it.slug.replace(TIMESTAMP_REGEX, "") },
valueTransform = HeanCmsSeriesDto::slug valueTransform = {
HeanCmsTitle(
it.slug,
it.thumbnail,
it.status.orEmpty()
)
}
) )
} }
@ -327,6 +365,7 @@ abstract class HeanCms(
val genres = getGenreList() val genres = getGenreList()
val filters = listOfNotNull( val filters = listOfNotNull(
Filter.Header(intl.filterWarning),
StatusFilter(intl.statusFilterTitle, getStatusList()), StatusFilter(intl.statusFilterTitle, getStatusList()),
SortByFilter(intl.sortByFilterTitle, getSortProperties()), SortByFilter(intl.sortByFilterTitle, getSortProperties()),
GenreFilter(intl.genreFilterTitle, genres).takeIf { genres.isNotEmpty() } GenreFilter(intl.genreFilterTitle, genres).takeIf { genres.isNotEmpty() }
@ -352,4 +391,10 @@ abstract class HeanCms(
val TIMESTAMP_REGEX = "-\\d+$".toRegex() val TIMESTAMP_REGEX = "-\\d+$".toRegex()
} }
data class HeanCmsTitle(
val slug: String,
val thumbnailUrl: String,
val status: String
)
} }

View File

@ -9,13 +9,13 @@ import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@Serializable @Serializable
data class HeanCmsSearchDto( data class HeanCmsQuerySearchDto(
val data: List<HeanCmsSeriesDto> = emptyList(), val data: List<HeanCmsSeriesDto> = emptyList(),
val meta: HeanCmsSearchMetaDto? = null val meta: HeanCmsQuerySearchMetaDto? = null
) )
@Serializable @Serializable
data class HeanCmsSearchMetaDto( data class HeanCmsQuerySearchMetaDto(
@SerialName("current_page") val currentPage: Int, @SerialName("current_page") val currentPage: Int,
@SerialName("last_page") val lastPage: Int @SerialName("last_page") val lastPage: Int
) { ) {
@ -24,6 +24,14 @@ data class HeanCmsSearchMetaDto(
get() = currentPage < lastPage get() = currentPage < lastPage
} }
@Serializable
data class HeanCmsSearchDto(
val description: String? = null,
@SerialName("series_slug") val slug: String,
@SerialName("series_type") val type: String,
val title: String
)
@Serializable @Serializable
data class HeanCmsSeriesDto( data class HeanCmsSeriesDto(
val id: Int, val id: Int,
@ -100,7 +108,7 @@ data class HeanCmsReaderContentDto(
) )
@Serializable @Serializable
data class HeanCmsSearchPayloadDto( data class HeanCmsQuerySearchPayloadDto(
val order: String, val order: String,
val page: Int, val page: Int,
@SerialName("order_by") val orderBy: String, @SerialName("order_by") val orderBy: String,
@ -108,3 +116,8 @@ data class HeanCmsSearchPayloadDto(
@SerialName("series_type") val type: String, @SerialName("series_type") val type: String,
@SerialName("tags_ids") val tagIds: List<Int> = emptyList() @SerialName("tags_ids") val tagIds: List<Int> = emptyList()
) )
@Serializable
data class HeanCmsSearchPayloadDto(
val term: String,
)

View File

@ -9,7 +9,7 @@ class HeanCmsGenerator : ThemeSourceGenerator {
override val themeClass = "HeanCms" override val themeClass = "HeanCms"
override val baseVersionCode: Int = 6 override val baseVersionCode: Int = 7
override val sources = listOf( override val sources = listOf(
SingleLang("Reaper Scans", "https://reaperscans.net", "pt-BR", overrideVersionCode = 35), SingleLang("Reaper Scans", "https://reaperscans.net", "pt-BR", overrideVersionCode = 35),

View File

@ -64,6 +64,12 @@ class HeanCmsIntl(lang: String) {
else -> "Recently added" else -> "Recently added"
} }
val filterWarning: String = when (availableLang) {
BRAZILIAN_PORTUGUESE -> "Os filtros serão ignorados se a busca não estiver vazia."
SPANISH -> "Los filtros serán ignorados si la búsqueda no está vacía."
else -> "Filters will be ignored if the search is not empty."
}
fun urlChangedError(sourceName: String): String = when (availableLang) { fun urlChangedError(sourceName: String): String = when (availableLang) {
BRAZILIAN_PORTUGUESE -> BRAZILIAN_PORTUGUESE ->
"A URL da série mudou. Migre de $sourceName " + "A URL da série mudou. Migre de $sourceName " +