diff --git a/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt b/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt index 50c411975..b380bcc9e 100644 --- a/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt +++ b/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt @@ -19,6 +19,10 @@ class ReaperScans : HeanCms( // Site changed from Madara to HeanCms. override val versionId = 2 + override val fetchAllTitles = true + + override val coverPath: String = "" + override fun getGenreList(): List = listOf( Genre("Artes Marciais", 2), Genre("Aventura", 10), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt index 59c3a5d9c..092beba65 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt @@ -37,6 +37,10 @@ abstract class HeanCms( protected val intl by lazy { HeanCmsIntl(lang) } + protected open val fetchAllTitles: Boolean = false + + protected open val coverPath: String = "cover/" + private var seriesSlugMap: Map? = null override fun headersBuilder(): Headers.Builder = Headers.Builder() @@ -44,7 +48,8 @@ abstract class HeanCms( .add("Referer", "$baseUrl/") override fun popularMangaRequest(page: Int): Request { - val payloadObj = HeanCmsSearchDto( + val payloadObj = HeanCmsSearchPayloadDto( + page = page, order = "desc", orderBy = "total_views", status = "Ongoing", @@ -62,8 +67,19 @@ abstract class HeanCms( } override fun popularMangaParse(response: Response): MangasPage { + val json = response.body?.string().orEmpty() + + if (json.startsWith("{")) { + val result = json.parseAs() + val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } + + fetchAllTitles() + + return MangasPage(mangaList, result.meta?.hasNextPage ?: false) + } + val mangaList = response.parseAs>() - .map { it.toSManga(apiUrl) } + .map { it.toSManga(apiUrl, coverPath) } fetchAllTitles() @@ -71,7 +87,8 @@ abstract class HeanCms( } override fun latestUpdatesRequest(page: Int): Request { - val payloadObj = HeanCmsSearchDto( + val payloadObj = HeanCmsSearchPayloadDto( + page = page, order = "desc", orderBy = "latest", status = "Ongoing", @@ -93,7 +110,8 @@ abstract class HeanCms( override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { val sortByFilter = filters.firstInstanceOrNull() - val payloadObj = HeanCmsSearchDto( + val payloadObj = HeanCmsSearchPayloadDto( + page = page, order = if (sortByFilter?.state?.ascending == true) "asc" else "desc", orderBy = sortByFilter?.selected ?: "total_views", status = filters.firstInstanceOrNull()?.selected?.value ?: "Ongoing", @@ -119,10 +137,24 @@ abstract class HeanCms( } override fun searchMangaParse(response: Response): MangasPage { + val json = response.body?.string().orEmpty() val query = response.request.url.queryParameter("q").orEmpty() + if (json.startsWith("{")) { + val result = json.parseAs() + var mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } + + if (query.isNotBlank()) { + mangaList = mangaList.filter { it.title.contains(query, ignoreCase = true) } + } + + fetchAllTitles() + + return MangasPage(mangaList, result.meta?.hasNextPage ?: false) + } + var mangaList = response.parseAs>() - .map { it.toSManga(apiUrl) } + .map { it.toSManga(apiUrl, coverPath) } if (query.isNotBlank()) { mangaList = mangaList.filter { it.title.contains(query, ignoreCase = true) } @@ -158,7 +190,7 @@ abstract class HeanCms( override fun mangaDetailsParse(response: Response): SManga { val result = runCatching { response.parseAs() } - val seriesDetails = result.getOrNull()?.toSManga(apiUrl) + val seriesDetails = result.getOrNull()?.toSManga(apiUrl, coverPath) ?: throw Exception(intl.urlChangedError(name)) return seriesDetails.apply { @@ -220,20 +252,40 @@ abstract class HeanCms( protected open fun getGenreList(): List = emptyList() protected open fun fetchAllTitles() { - if (!seriesSlugMap.isNullOrEmpty()) { + if (!seriesSlugMap.isNullOrEmpty() || !fetchAllTitles) { return } val result = runCatching { - client.newCall(allTitlesRequest()).execute() - .let { parseAllTitles(it) } + var hasNextPage = true + var page = 1 + val tempMap = mutableMapOf() + + while (hasNextPage) { + val response = client.newCall(allTitlesRequest(page)).execute() + val json = response.body?.string().orEmpty() + + if (json.startsWith("{")) { + val result = json.parseAs() + tempMap.putAll(parseAllTitles(result.data)) + hasNextPage = result.meta?.hasNextPage ?: false + page++ + } else { + val result = json.parseAs>() + tempMap.putAll(parseAllTitles(result)) + hasNextPage = false + } + } + + tempMap.toMap() } seriesSlugMap = result.getOrNull() } - protected open fun allTitlesRequest(): Request { - val payloadObj = HeanCmsSearchDto( + protected open fun allTitlesRequest(page: Int): Request { + val payloadObj = HeanCmsSearchPayloadDto( + page = page, order = "desc", orderBy = "total_views", status = "", @@ -250,8 +302,8 @@ abstract class HeanCms( return POST("$apiUrl/series/querysearch", apiHeaders, payload) } - protected open fun parseAllTitles(response: Response): Map { - return response.parseAs>() + protected open fun parseAllTitles(result: List): Map { + return result .filter { it.type == "Comic" } .associateBy( keySelector = { it.slug.replace(TIMESTAMP_REGEX, "") }, @@ -275,6 +327,8 @@ abstract class HeanCms( json.decodeFromString(it.body?.string().orEmpty()) } + private inline fun String.parseAs(): T = json.decodeFromString(this) + private inline fun List<*>.firstInstanceOrNull(): R? = filterIsInstance().firstOrNull() diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt index 1c6f6d48b..a9e86a890 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt @@ -8,6 +8,22 @@ import org.jsoup.Jsoup import java.text.SimpleDateFormat import java.util.Locale +@Serializable +data class HeanCmsSearchDto( + val data: List = emptyList(), + val meta: HeanCmsSearchMetaDto? = null +) + +@Serializable +data class HeanCmsSearchMetaDto( + @SerialName("current_page") val currentPage: Int, + @SerialName("last_page") val lastPage: Int +) { + + val hasNextPage: Boolean + get() = currentPage < lastPage +} + @Serializable data class HeanCmsSeriesDto( val id: Int, @@ -23,7 +39,7 @@ data class HeanCmsSeriesDto( val chapters: List? = emptyList() ) { - fun toSManga(apiUrl: String): SManga = SManga.create().apply { + fun toSManga(apiUrl: String, coverPath: String): SManga = SManga.create().apply { val descriptionBody = this@HeanCmsSeriesDto.description?.let(Jsoup::parseBodyFragment) title = this@HeanCmsSeriesDto.title @@ -35,7 +51,7 @@ data class HeanCmsSeriesDto( genre = tags.orEmpty() .sortedBy(HeanCmsTagDto::name) .joinToString { it.name } - thumbnail_url = "$apiUrl/cover/$thumbnail" + thumbnail_url = "$apiUrl/$coverPath$thumbnail" status = when (this@HeanCmsSeriesDto.status) { "Ongoing" -> SManga.ONGOING "Hiatus" -> SManga.ON_HIATUS @@ -84,8 +100,9 @@ data class HeanCmsReaderContentDto( ) @Serializable -data class HeanCmsSearchDto( +data class HeanCmsSearchPayloadDto( val order: String, + val page: Int, @SerialName("order_by") val orderBy: String, @SerialName("series_status") val status: String, @SerialName("series_type") val type: String, diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt index a149fb17d..d5f4c7436 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt @@ -9,7 +9,7 @@ class HeanCmsGenerator : ThemeSourceGenerator { override val themeClass = "HeanCms" - override val baseVersionCode: Int = 1 + override val baseVersionCode: Int = 2 override val sources = listOf( SingleLang("Reaper Scans", "https://reaperscans.net", "pt-BR", overrideVersionCode = 35),