diff --git a/src/pt/saikaiscan/build.gradle b/src/pt/saikaiscan/build.gradle index 3009cff3b..a3fc8f984 100644 --- a/src/pt/saikaiscan/build.gradle +++ b/src/pt/saikaiscan/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Saikai Scan' pkgNameSuffix = 'pt.saikaiscan' extClass = '.SaikaiScan' - extVersionCode = 9 + extVersionCode = 10 } apply from: "$rootDir/common.gradle" diff --git a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScan.kt b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScan.kt index b9d580b0a..adae7acc6 100644 --- a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScan.kt +++ b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScan.kt @@ -16,17 +16,14 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response -import org.jsoup.Jsoup import rx.Observable import uy.kohesive.injekt.injectLazy -import java.text.SimpleDateFormat -import java.util.Locale class SaikaiScan : HttpSource() { - override val name = "Saikai Scan" + override val name = SOURCE_NAME - override val baseUrl = "https://saikaiscan.com.br" + override val baseUrl = "https://saikai.com.br" override val lang = "pt-BR" @@ -63,18 +60,12 @@ class SaikaiScan : HttpSource() { override fun popularMangaParse(response: Response): MangasPage { val result = response.parseAs() - val mangaList = result.data!!.map(::popularMangaFromObject) + val mangaList = result.data!!.map(SaikaiScanStoryDto::toSManga) val hasNextPage = result.meta!!.currentPage < result.meta.lastPage return MangasPage(mangaList, hasNextPage) } - private fun popularMangaFromObject(obj: SaikaiScanStoryDto): SManga = SManga.create().apply { - title = obj.title - thumbnail_url = "$IMAGE_SERVER_URL/${obj.image}" - url = "/comics/${obj.slug}" - } - override fun latestUpdatesRequest(page: Int): Request { val apiHeaders = headersBuilder() .add("Accept", ACCEPT_JSON) @@ -106,35 +97,8 @@ class SaikaiScan : HttpSource() { .addQueryParameter("per_page", PER_PAGE) .addQueryParameter("relationships", "language,type,format") - filters.forEach { filter -> - when (filter) { - is GenreFilter -> { - val genresParameter = filter.state - .filter { it.state } - .joinToString(",") { it.id.toString() } - apiEndpointUrl.addQueryParameter("genres", genresParameter) - } - - is CountryFilter -> { - if (filter.state > 0) { - apiEndpointUrl.addQueryParameter("country", filter.selected.id.toString()) - } - } - - is StatusFilter -> { - if (filter.state > 0) { - apiEndpointUrl.addQueryParameter("status", filter.selected.id.toString()) - } - } - - is SortByFilter -> { - val sortProperty = filter.sortProperties[filter.state!!.index] - val sortDirection = if (filter.state!!.ascending) "asc" else "desc" - apiEndpointUrl.setQueryParameter("sortProperty", sortProperty.slug) - apiEndpointUrl.setQueryParameter("sortDirection", sortDirection) - } - } - } + filters.filterIsInstance() + .forEach { it.addQueryParameter(apiEndpointUrl) } return GET(apiEndpointUrl.toString(), apiHeaders) } @@ -167,19 +131,10 @@ class SaikaiScan : HttpSource() { return GET(apiEndpointUrl, apiHeaders) } - override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply { + override fun mangaDetailsParse(response: Response): SManga { val result = response.parseAs() - val story = result.data!![0] - title = story.title - author = story.authors.joinToString { it.name } - artist = story.artists.joinToString { it.name } - thumbnail_url = "$IMAGE_SERVER_URL/${story.image}" - genre = story.genres.joinToString { it.name } - status = story.status!!.name.toStatus() - description = Jsoup.parse(story.synopsis) - .select("p") - .joinToString("\n\n") { it.text() } + return result.data!![0].toSManga() } override fun chapterListRequest(manga: SManga): Request { @@ -205,20 +160,10 @@ class SaikaiScan : HttpSource() { return story.releases .filter { it.isActive == 1 } - .map { chapterFromObject(it, story.slug) } + .map { it.toSChapter(story.slug) } .sortedByDescending(SChapter::chapter_number) } - private fun chapterFromObject(obj: SaikaiScanReleaseDto, storySlug: String): SChapter = - SChapter.create().apply { - name = "Capítulo ${obj.chapter}" + - (if (obj.title.isNullOrEmpty().not()) " - ${obj.title}" else "") - chapter_number = obj.chapter.toFloatOrNull() ?: -1f - date_upload = obj.publishedAt.toDate() - scanlator = this@SaikaiScan.name - url = "/ler/comics/$storySlug/${obj.id}/${obj.slug}" - } - override fun pageListRequest(chapter: SChapter): Request { val releaseId = chapter.url .substringBeforeLast("/") @@ -347,29 +292,16 @@ class SaikaiScan : HttpSource() { json.decodeFromString(it.body?.string().orEmpty()) } - private fun String.toDate(): Long { - return runCatching { DATE_FORMATTER.parse(this)?.time } - .getOrNull() ?: 0L - } - - private fun String.toStatus(): Int = when (this) { - "Concluído" -> SManga.COMPLETED - "Em Andamento" -> SManga.ONGOING - else -> SManga.UNKNOWN - } - companion object { + const val SOURCE_NAME = "Saikai Scan" + 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 COMIC_FORMAT_ID = "2" private const val PER_PAGE = "12" - private val DATE_FORMATTER by lazy { - SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'", Locale("pt", "BR")) - } - private const val API_URL = "https://api.saikai.com.br" - private const val IMAGE_SERVER_URL = "https://s3-alpha.saikai.com.br" + const val IMAGE_SERVER_URL = "https://s3-alpha.saikai.com.br" } } diff --git a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanDto.kt b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanDto.kt index e1bfc66f0..a820c21da 100644 --- a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanDto.kt +++ b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanDto.kt @@ -1,7 +1,12 @@ package eu.kanade.tachiyomi.extension.pt.saikaiscan +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import org.jsoup.Jsoup +import java.text.SimpleDateFormat +import java.util.Locale @Serializable data class SaikaiScanResultDto( @@ -29,7 +34,25 @@ data class SaikaiScanStoryDto( val status: SaikaiScanStatusDto? = null, val synopsis: String, val title: String -) +) { + + fun toSManga(): SManga = SManga.create().apply { + title = this@SaikaiScanStoryDto.title + author = authors.joinToString { it.name } + artist = artists.joinToString { it.name } + genre = genres.joinToString { it.name } + status = when (this@SaikaiScanStoryDto.status?.name) { + "Concluído" -> SManga.COMPLETED + "Em Andamento" -> SManga.ONGOING + else -> SManga.UNKNOWN + } + description = Jsoup.parseBodyFragment(synopsis) + .select("p") + .joinToString("\n\n") { it.text() } + thumbnail_url = "${SaikaiScan.IMAGE_SERVER_URL}/$image" + url = "/comics/$slug" + } +} @Serializable data class SaikaiScanPersonDto( @@ -55,9 +78,24 @@ data class SaikaiScanReleaseDto( @SerialName("release_images") val releaseImages: List = emptyList(), val slug: String, val title: String? = "" -) +) { + + fun toSChapter(storySlug: String): SChapter = SChapter.create().apply { + name = "Capítulo $chapter" + + (if (this@SaikaiScanReleaseDto.title.isNullOrEmpty().not()) " - ${this@SaikaiScanReleaseDto.title}" else "") + chapter_number = chapter.toFloatOrNull() ?: -1f + date_upload = runCatching { DATE_FORMATTER.parse(publishedAt)?.time } + .getOrNull() ?: 0L + scanlator = SaikaiScan.SOURCE_NAME + url = "/ler/comics/$storySlug/$id/$slug" + } + + companion object { + private val DATE_FORMATTER by lazy { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'", Locale("pt", "BR")) + } + } +} @Serializable -data class SaikaiScanReleaseImageDto( - val image: String -) +data class SaikaiScanReleaseImageDto(val image: String) diff --git a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanFilters.kt b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanFilters.kt index 580a0f113..49db990ff 100644 --- a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanFilters.kt +++ b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanFilters.kt @@ -1,10 +1,26 @@ package eu.kanade.tachiyomi.extension.pt.saikaiscan import eu.kanade.tachiyomi.source.model.Filter +import okhttp3.HttpUrl + +interface UrlQueryFilter { + fun addQueryParameter(url: HttpUrl.Builder) +} class Genre(title: String, val id: Int) : Filter.CheckBox(title) -class GenreFilter(genres: List) : Filter.Group("Gêneros", genres) +class GenreFilter(genres: List) : + Filter.Group("Gêneros", genres), + UrlQueryFilter { + + override fun addQueryParameter(url: HttpUrl.Builder) { + val genresParameter = state + .filter { it.state } + .joinToString(",") { it.id.toString() } + + url.addQueryParameter("genres", genresParameter) + } +} data class Country(val name: String, val id: Int) { override fun toString(): String = name @@ -15,26 +31,48 @@ open class EnhancedSelect(name: String, values: Array) : Filter.Select( get() = values[state] } -class CountryFilter(countries: List) : EnhancedSelect( - "Nacionalidade", - countries.toTypedArray() -) +class CountryFilter(countries: List) : + EnhancedSelect("Nacionalidade", countries.toTypedArray()), + UrlQueryFilter { + + override fun addQueryParameter(url: HttpUrl.Builder) { + if (state > 0) { + url.addQueryParameter("country", selected.id.toString()) + } + } +} data class Status(val name: String, val id: Int) { override fun toString(): String = name } -class StatusFilter(statuses: List) : EnhancedSelect( - "Status", - statuses.toTypedArray() -) +class StatusFilter(statuses: List) : + EnhancedSelect("Status", statuses.toTypedArray()), + UrlQueryFilter { + + override fun addQueryParameter(url: HttpUrl.Builder) { + if (state > 0) { + url.addQueryParameter("status", selected.id.toString()) + } + } +} data class SortProperty(val name: String, val slug: String) { override fun toString(): String = name } -class SortByFilter(val sortProperties: List) : Filter.Sort( - "Ordenar por", - sortProperties.map { it.name }.toTypedArray(), - Selection(2, ascending = false) -) +class SortByFilter(val sortProperties: List) : + Filter.Sort( + name = "Ordenar por", + values = sortProperties.map { it.name }.toTypedArray(), + state = Selection(2, ascending = false) + ), + UrlQueryFilter { + + override fun addQueryParameter(url: HttpUrl.Builder) { + val sortProperty = sortProperties[state!!.index] + val sortDirection = if (state!!.ascending) "asc" else "desc" + url.setQueryParameter("sortProperty", sortProperty.slug) + url.setQueryParameter("sortDirection", sortDirection) + } +}