Refactor and fix some issues in HeanCms. (#14887)

Co-authored-by: Seew <90949336+seew3l@users.noreply.github.com>
This commit is contained in:
Alessandro Jean 2023-01-10 17:19:02 -03:00 committed by GitHub
parent a00ed03e63
commit 2bb71b034d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 62 deletions

View File

@ -14,7 +14,6 @@ import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
@ -109,6 +108,23 @@ abstract class HeanCms(
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
/**
* Their query search endpoint doesn't return the thumbnails, so we need to do
* later an special parsing to get the thumbnails as well from the slug map.
*/
if (query.isNotBlank()) {
val searchPayloadObj = HeanCmsSearchPayloadDto(query)
val searchPayload = json.encodeToString(searchPayloadObj)
.toRequestBody(JSON_MEDIA_TYPE)
val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
.add("Content-Type", searchPayload.contentType().toString())
.build()
return POST("$apiUrl/series/search", apiHeaders, searchPayload)
}
val sortByFilter = filters.firstInstanceOrNull<SortByFilter>()
val payloadObj = HeanCmsQuerySearchPayloadDto(
@ -123,42 +139,26 @@ abstract class HeanCms(
.orEmpty()
)
val searchPayloadObj = HeanCmsSearchPayloadDto(
term = query
)
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
val searchPayload = json.encodeToString(searchPayloadObj).toRequestBody(JSON_MEDIA_TYPE)
val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
.add("Content-Type", payload.contentType().toString())
.build()
if (query.isNotBlank()) {
val apiUrl = "$apiUrl/series/search".toHttpUrl().newBuilder()
.toString()
return POST(apiUrl, apiHeaders, searchPayload)
}
val apiUrl = "$apiUrl/series/querysearch".toHttpUrl().newBuilder()
.toString()
return POST(apiUrl, apiHeaders, payload)
return POST("$apiUrl/series/querysearch", apiHeaders, payload)
}
override fun searchMangaParse(response: Response): MangasPage {
val json = response.body?.string().orEmpty()
if (response.request.url.pathSegments.last() == "search") {
fetchAllTitles()
val result = json.parseAs<List<HeanCmsSearchDto>>()
val mangaList = result
.filter { it.type == "Comic" }
.map {
createSearchResult(it.slug.replace(TIMESTAMP_REGEX, ""), it.title)
}
fetchAllTitles()
.map { it.toSManga(apiUrl, coverPath, seriesSlugMap.orEmpty()) }
return MangasPage(mangaList, false)
}
@ -180,23 +180,6 @@ abstract class HeanCms(
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.
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(seriesDetailsRequest(manga))
@ -223,13 +206,15 @@ abstract class HeanCms(
fetchAllTitles()
val currentSlug = seriesSlugMap?.get(seriesSlug)?.slug ?: seriesSlug
val seriesDetails = seriesSlugMap?.get(seriesSlug)
val currentSlug = seriesDetails?.slug ?: seriesSlug
val currentStatus = seriesDetails?.status ?: SManga.UNKNOWN
val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
.build()
return GET("$apiUrl/series/$currentSlug#${manga.status}", apiHeaders)
return GET("$apiUrl/series/$currentSlug#$currentStatus", apiHeaders)
}
override fun mangaDetailsParse(response: Response): SManga {
@ -247,9 +232,11 @@ abstract class HeanCms(
override fun chapterListParse(response: Response): List<SChapter> {
val result = response.parseAs<HeanCmsSeriesDto>()
val seriesSlug = response.request.url.pathSegments.last()
val currentTimestamp = System.currentTimeMillis()
return result.chapters.orEmpty()
.map { it.toSChapter(seriesSlug) }
.filter { it.date_upload <= currentTimestamp }
.reversed()
}
@ -265,7 +252,9 @@ abstract class HeanCms(
override fun pageListParse(response: Response): List<Page> {
return response.parseAs<HeanCmsReaderDto>().content?.images.orEmpty()
.mapIndexed { i, url -> Page(i, "", "$apiUrl/$url") }
.mapIndexed { i, url ->
Page(i, imageUrl = if (url.startsWith("http")) url else "$apiUrl/$url")
}
}
override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl!!)
@ -353,9 +342,9 @@ abstract class HeanCms(
keySelector = { it.slug.replace(TIMESTAMP_REGEX, "") },
valueTransform = {
HeanCmsTitle(
it.slug,
it.thumbnail,
it.status.orEmpty()
slug = it.slug,
thumbnailFileName = it.thumbnail,
status = it.status?.toStatus() ?: SManga.UNKNOWN
)
}
)
@ -383,6 +372,12 @@ abstract class HeanCms(
private inline fun <reified R> List<*>.firstInstanceOrNull(): R? =
filterIsInstance<R>().firstOrNull()
/**
* Used to store the current slugs for sources that change it periodically and for the
* search that doesn't return the thumbnail URLs.
*/
data class HeanCmsTitle(val slug: String, val thumbnailFileName: String, val status: Int)
companion object {
private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
private const val ACCEPT_JSON = "application/json, text/plain, */*"
@ -391,10 +386,4 @@ abstract class HeanCms(
val TIMESTAMP_REGEX = "-\\d+$".toRegex()
}
data class HeanCmsTitle(
val slug: String,
val thumbnailUrl: String,
val status: String
)
}

View File

@ -30,7 +30,24 @@ data class HeanCmsSearchDto(
@SerialName("series_slug") val slug: String,
@SerialName("series_type") val type: String,
val title: String
)
) {
fun toSManga(
apiUrl: String,
coverPath: String,
slugMap: Map<String, HeanCms.HeanCmsTitle>
): SManga = SManga.create().apply {
val slugOnly = slug.replace(HeanCms.TIMESTAMP_REGEX, "")
val thumbnailFileName = slugMap[slugOnly]?.thumbnailFileName.orEmpty()
title = this@HeanCmsSearchDto.title
thumbnail_url = when {
thumbnailFileName.isNotEmpty() -> "$apiUrl/$coverPath$thumbnailFileName"
else -> ""
}
url = "/series/$slugOnly"
}
}
@Serializable
data class HeanCmsSeriesDto(
@ -60,13 +77,7 @@ data class HeanCmsSeriesDto(
.sortedBy(HeanCmsTagDto::name)
.joinToString { it.name }
thumbnail_url = "$apiUrl/$coverPath$thumbnail"
status = when (this@HeanCmsSeriesDto.status) {
"Ongoing" -> SManga.ONGOING
"Hiatus" -> SManga.ON_HIATUS
"Dropped" -> SManga.CANCELLED
"Completed", "Finished" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
status = this@HeanCmsSeriesDto.status?.toStatus() ?: SManga.UNKNOWN
url = "/series/${slug.replace(HeanCms.TIMESTAMP_REGEX, "")}"
}
}
@ -118,6 +129,12 @@ data class HeanCmsQuerySearchPayloadDto(
)
@Serializable
data class HeanCmsSearchPayloadDto(
val term: String,
)
data class HeanCmsSearchPayloadDto(val term: String)
fun String.toStatus(): Int = when (this) {
"Ongoing" -> SManga.ONGOING
"Hiatus" -> SManga.ON_HIATUS
"Dropped" -> SManga.CANCELLED
"Completed", "Finished" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}

View File

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