Refactor and fix some issues in HeanCms. (#14887)
Co-authored-by: Seew <90949336+seew3l@users.noreply.github.com>
This commit is contained in:
parent
a00ed03e63
commit
2bb71b034d
@ -14,7 +14,6 @@ import kotlinx.serialization.decodeFromString
|
|||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
@ -109,6 +108,23 @@ abstract class HeanCms(
|
|||||||
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
|
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
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 sortByFilter = filters.firstInstanceOrNull<SortByFilter>()
|
||||||
|
|
||||||
val payloadObj = HeanCmsQuerySearchPayloadDto(
|
val payloadObj = HeanCmsQuerySearchPayloadDto(
|
||||||
@ -123,42 +139,26 @@ 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()) {
|
return POST("$apiUrl/series/querysearch", apiHeaders, payload)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage {
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
val json = response.body?.string().orEmpty()
|
val json = response.body?.string().orEmpty()
|
||||||
|
|
||||||
if (response.request.url.pathSegments.last() == "search") {
|
if (response.request.url.pathSegments.last() == "search") {
|
||||||
|
fetchAllTitles()
|
||||||
|
|
||||||
val result = json.parseAs<List<HeanCmsSearchDto>>()
|
val result = json.parseAs<List<HeanCmsSearchDto>>()
|
||||||
val mangaList = result
|
val mangaList = result
|
||||||
.filter { it.type == "Comic" }
|
.filter { it.type == "Comic" }
|
||||||
.map {
|
.map { it.toSManga(apiUrl, coverPath, seriesSlugMap.orEmpty()) }
|
||||||
createSearchResult(it.slug.replace(TIMESTAMP_REGEX, ""), it.title)
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchAllTitles()
|
|
||||||
|
|
||||||
return MangasPage(mangaList, false)
|
return MangasPage(mangaList, false)
|
||||||
}
|
}
|
||||||
@ -180,23 +180,6 @@ abstract class HeanCms(
|
|||||||
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))
|
||||||
@ -223,13 +206,15 @@ abstract class HeanCms(
|
|||||||
|
|
||||||
fetchAllTitles()
|
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()
|
val apiHeaders = headersBuilder()
|
||||||
.add("Accept", ACCEPT_JSON)
|
.add("Accept", ACCEPT_JSON)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return GET("$apiUrl/series/$currentSlug#${manga.status}", apiHeaders)
|
return GET("$apiUrl/series/$currentSlug#$currentStatus", apiHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mangaDetailsParse(response: Response): SManga {
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
@ -247,9 +232,11 @@ abstract class HeanCms(
|
|||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val result = response.parseAs<HeanCmsSeriesDto>()
|
val result = response.parseAs<HeanCmsSeriesDto>()
|
||||||
val seriesSlug = response.request.url.pathSegments.last()
|
val seriesSlug = response.request.url.pathSegments.last()
|
||||||
|
val currentTimestamp = System.currentTimeMillis()
|
||||||
|
|
||||||
return result.chapters.orEmpty()
|
return result.chapters.orEmpty()
|
||||||
.map { it.toSChapter(seriesSlug) }
|
.map { it.toSChapter(seriesSlug) }
|
||||||
|
.filter { it.date_upload <= currentTimestamp }
|
||||||
.reversed()
|
.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +252,9 @@ abstract class HeanCms(
|
|||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
return response.parseAs<HeanCmsReaderDto>().content?.images.orEmpty()
|
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!!)
|
override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl!!)
|
||||||
@ -353,9 +342,9 @@ abstract class HeanCms(
|
|||||||
keySelector = { it.slug.replace(TIMESTAMP_REGEX, "") },
|
keySelector = { it.slug.replace(TIMESTAMP_REGEX, "") },
|
||||||
valueTransform = {
|
valueTransform = {
|
||||||
HeanCmsTitle(
|
HeanCmsTitle(
|
||||||
it.slug,
|
slug = it.slug,
|
||||||
it.thumbnail,
|
thumbnailFileName = it.thumbnail,
|
||||||
it.status.orEmpty()
|
status = it.status?.toStatus() ?: SManga.UNKNOWN
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -383,6 +372,12 @@ abstract class HeanCms(
|
|||||||
private inline fun <reified R> List<*>.firstInstanceOrNull(): R? =
|
private inline fun <reified R> List<*>.firstInstanceOrNull(): R? =
|
||||||
filterIsInstance<R>().firstOrNull()
|
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 {
|
companion object {
|
||||||
private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
|
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, */*"
|
private const val ACCEPT_JSON = "application/json, text/plain, */*"
|
||||||
@ -391,10 +386,4 @@ 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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,24 @@ data class HeanCmsSearchDto(
|
|||||||
@SerialName("series_slug") val slug: String,
|
@SerialName("series_slug") val slug: String,
|
||||||
@SerialName("series_type") val type: String,
|
@SerialName("series_type") val type: String,
|
||||||
val title: 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
|
@Serializable
|
||||||
data class HeanCmsSeriesDto(
|
data class HeanCmsSeriesDto(
|
||||||
@ -60,13 +77,7 @@ data class HeanCmsSeriesDto(
|
|||||||
.sortedBy(HeanCmsTagDto::name)
|
.sortedBy(HeanCmsTagDto::name)
|
||||||
.joinToString { it.name }
|
.joinToString { it.name }
|
||||||
thumbnail_url = "$apiUrl/$coverPath$thumbnail"
|
thumbnail_url = "$apiUrl/$coverPath$thumbnail"
|
||||||
status = when (this@HeanCmsSeriesDto.status) {
|
status = this@HeanCmsSeriesDto.status?.toStatus() ?: SManga.UNKNOWN
|
||||||
"Ongoing" -> SManga.ONGOING
|
|
||||||
"Hiatus" -> SManga.ON_HIATUS
|
|
||||||
"Dropped" -> SManga.CANCELLED
|
|
||||||
"Completed", "Finished" -> SManga.COMPLETED
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
url = "/series/${slug.replace(HeanCms.TIMESTAMP_REGEX, "")}"
|
url = "/series/${slug.replace(HeanCms.TIMESTAMP_REGEX, "")}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,6 +129,12 @@ data class HeanCmsQuerySearchPayloadDto(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class HeanCmsSearchPayloadDto(
|
data class HeanCmsSearchPayloadDto(val term: String)
|
||||||
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
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ class HeanCmsGenerator : ThemeSourceGenerator {
|
|||||||
|
|
||||||
override val themeClass = "HeanCms"
|
override val themeClass = "HeanCms"
|
||||||
|
|
||||||
override val baseVersionCode: Int = 7
|
override val baseVersionCode: Int = 8
|
||||||
|
|
||||||
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),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user