diff --git a/src/pt/reaperscans/build.gradle b/src/pt/reaperscans/build.gradle index dfa02ff1d..d0178481d 100644 --- a/src/pt/reaperscans/build.gradle +++ b/src/pt/reaperscans/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Reaper Scans' pkgNameSuffix = 'pt.reaperscans' extClass = '.ReaperScans' - extVersionCode = 31 + extVersionCode = 32 } apply from: "$rootDir/common.gradle" diff --git a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScans.kt b/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScans.kt index 51d90342a..cd91ca0a9 100644 --- a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScans.kt +++ b/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScans.kt @@ -11,10 +11,8 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.put import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaType @@ -49,15 +47,14 @@ class ReaperScans : HttpSource() { .add("Referer", "$baseUrl/") override fun popularMangaRequest(page: Int): Request { - val payloadObj = buildJsonObject { - put("order", "desc") - put("order_by", "total_views") - put("series_status", "Ongoing") - put("series_type", "Comic") - put("tag_ids", JsonArray(emptyList())) - } + val payloadObj = ReaperSearchDto( + order = "desc", + orderBy = "total_views", + status = "Ongoing", + type = "Comic" + ) - val payload = payloadObj.toString().toRequestBody(JSON_MEDIA_TYPE) + val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE) val apiHeaders = headersBuilder() .add("Accept", ACCEPT_JSON) @@ -75,15 +72,14 @@ class ReaperScans : HttpSource() { } override fun latestUpdatesRequest(page: Int): Request { - val payloadObj = buildJsonObject { - put("order", "desc") - put("order_by", "latest") - put("series_status", "Ongoing") - put("series_type", "Comic") - put("tag_ids", JsonArray(emptyList())) - } + val payloadObj = ReaperSearchDto( + order = "desc", + orderBy = "latest", + status = "Ongoing", + type = "Comic" + ) - val payload = payloadObj.toString().toRequestBody(JSON_MEDIA_TYPE) + val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE) val apiHeaders = headersBuilder() .add("Accept", ACCEPT_JSON) @@ -96,22 +92,20 @@ class ReaperScans : HttpSource() { override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response) override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val sortByFilter = filters.filterIsInstance().firstOrNull() - val sortAscending = sortByFilter?.state?.ascending ?: false - val sortProperty = sortByFilter?.selected ?: "total_views" + val sortByFilter = filters.firstInstanceOrNull() - val status = filters.filterIsInstance() - .firstOrNull()?.selected?.value ?: "Ongoing" + val payloadObj = ReaperSearchDto( + order = if (sortByFilter?.state?.ascending == true) "asc" else "desc", + orderBy = sortByFilter?.selected ?: "total_views", + status = filters.firstInstanceOrNull()?.selected?.value ?: "Ongoing", + type = "Comic", + tagIds = filters.firstInstanceOrNull()?.state + ?.filter(Genre::state) + ?.map(Genre::id) + .orEmpty() + ) - val payloadObj = buildJsonObject { - put("order", if (sortAscending) "asc" else "desc") - put("order_by", sortProperty) - put("series_status", status) - put("series_type", "Comic") - put("tag_ids", JsonArray(emptyList())) - } - - val payload = payloadObj.toString().toRequestBody(JSON_MEDIA_TYPE) + val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE) val apiHeaders = headersBuilder() .add("Accept", ACCEPT_JSON) @@ -213,15 +207,46 @@ class ReaperScans : HttpSource() { SortProperty("Data de criação", "latest") ) + private fun getGenreList(): List = listOf( + Genre("Artes Marciais", 2), + Genre("Aventura", 10), + Genre("Ação", 9), + Genre("Comédia", 14), + Genre("Drama", 15), + Genre("Escolar", 7), + Genre("Fantasia", 11), + Genre("Ficção científica", 16), + Genre("Guerra", 17), + Genre("Isekai", 18), + Genre("Jogo", 12), + Genre("Mangá", 24), + Genre("Manhua", 23), + Genre("Manhwa", 22), + Genre("Mecha", 19), + Genre("Mistério", 20), + Genre("Nacional", 8), + Genre("Realidade Virtual", 21), + Genre("Retorno", 3), + Genre("Romance", 5), + Genre("Segunda vida", 4), + Genre("Seinen", 1), + Genre("Shounen", 13), + Genre("Terror", 6) + ) + override fun getFilterList(): FilterList = FilterList( StatusFilter(getStatusList()), SortByFilter(getSortProperties()), + GenreFilter(getGenreList()) ) private inline fun Response.parseAs(): T = use { json.decodeFromString(it.body?.string().orEmpty()) } + private inline fun List<*>.firstInstanceOrNull(): R? = + filterIsInstance().firstOrNull() + 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, */*" diff --git a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansDto.kt b/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansDto.kt index fe095ea65..89d6b73ec 100644 --- a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansDto.kt +++ b/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansDto.kt @@ -18,25 +18,35 @@ data class ReaperSeriesDto( val status: String? = null, val thumbnail: String, val title: String, + val tags: List? = emptyList(), val chapters: List? = emptyList() ) { fun toSManga(): SManga = SManga.create().apply { title = this@ReaperSeriesDto.title - author = this@ReaperSeriesDto.author - artist = this@ReaperSeriesDto.studio - description = this@ReaperSeriesDto.description?.let { Jsoup.parseBodyFragment(it).text() } + author = this@ReaperSeriesDto.author?.trim() + artist = this@ReaperSeriesDto.studio?.trim() + description = this@ReaperSeriesDto.description + ?.let { Jsoup.parseBodyFragment(it).select("p") } + ?.joinToString("\n\n") { it.text() } + genre = tags.orEmpty() + .sortedBy(ReaperTagDto::name) + .joinToString { it.name } thumbnail_url = "${ReaperScans.API_URL}/cover/$thumbnail" status = when (this@ReaperSeriesDto.status) { "Ongoing" -> SManga.ONGOING "Hiatus" -> SManga.ON_HIATUS "Dropped" -> SManga.CANCELLED + "Completed", "Finished" -> SManga.COMPLETED else -> SManga.UNKNOWN } url = "/series/$slug" } } +@Serializable +data class ReaperTagDto(val name: String) + @Serializable data class ReaperChapterDto( val id: Int, @@ -47,7 +57,7 @@ data class ReaperChapterDto( ) { fun toSChapter(seriesSlug: String): SChapter = SChapter.create().apply { - name = this@ReaperChapterDto.name + name = this@ReaperChapterDto.name.trim() date_upload = runCatching { DATE_FORMAT.parse(createdAt.substringBefore("."))?.time } .getOrNull() ?: 0L url = "/series/$seriesSlug/$slug#$id" @@ -69,3 +79,12 @@ data class ReaperReaderDto( data class ReaperReaderContentDto( val images: List? = emptyList() ) + +@Serializable +data class ReaperSearchDto( + val order: String, + @SerialName("order_by") val orderBy: String, + @SerialName("series_status") val status: String, + @SerialName("series_type") val type: String, + @SerialName("tags_ids") val tagIds: List = emptyList() +) diff --git a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansFilters.kt b/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansFilters.kt index 87cde28e8..5a4c451c6 100644 --- a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansFilters.kt +++ b/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansFilters.kt @@ -2,6 +2,10 @@ package eu.kanade.tachiyomi.extension.pt.reaperscans import eu.kanade.tachiyomi.source.model.Filter +class Genre(title: String, val id: Int) : Filter.CheckBox(title) + +class GenreFilter(genres: List) : Filter.Group("Gêneros", genres) + open class EnhancedSelect(name: String, values: Array) : Filter.Select(name, values) { val selected: T get() = values[state]