diff --git a/src/es/olympusscanlation/build.gradle b/src/es/olympusscanlation/build.gradle index af8ed667d..b18bdd111 100644 --- a/src/es/olympusscanlation/build.gradle +++ b/src/es/olympusscanlation/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Olympus Scanlation' extClass = '.OlympusScanlation' - extVersionCode = 6 + extVersionCode = 7 } apply from: "$rootDir/common.gradle" diff --git a/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt index fcfcf3b5f..3022fdeb3 100644 --- a/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt +++ b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt @@ -18,13 +18,14 @@ import uy.kohesive.injekt.injectLazy import java.text.SimpleDateFormat import java.util.Locale import java.util.TimeZone +import kotlin.concurrent.thread class OlympusScanlation : HttpSource() { override val versionId = 2 - override val baseUrl: String = "https://olympusvisor.com" - private val apiBaseUrl: String = "https://dashboard.olympusvisor.com" + override val baseUrl: String = "https://visorolym.com" + private val apiBaseUrl: String = "https://dashboard.visorolym.com" override val lang: String = "es" override val name: String = "Olympus Scanlation" @@ -37,27 +38,21 @@ class OlympusScanlation : HttpSource() { .build() private val json: Json by injectLazy() + private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'", Locale.US).apply { timeZone = TimeZone.getTimeZone("UTC") } override fun popularMangaRequest(page: Int): Request { - val apiUrl = "$apiBaseUrl/api/series?page=1&direction=asc&type=comic".toHttpUrl().newBuilder() + val apiUrl = "$apiBaseUrl/api/sf/home".toHttpUrl().newBuilder() .build() return GET(apiUrl, headers) } override fun popularMangaParse(response: Response): MangasPage { - runCatching { fetchFilters() } - val result = json.decodeFromString(response.body.string()) - val resultMangaList = json.decodeFromString>(result.data.recommended_series) - val mangaList = resultMangaList.filter { it.type == "comic" }.map { - SManga.create().apply { - url = "/series/comic-${it.slug}" - title = it.name - thumbnail_url = it.cover - } - } + val result = json.decodeFromString(response.body.string()) + val popularJson = json.decodeFromString>(result.data.popularComics) + val mangaList = popularJson.filter { it.type == "comic" }.map { it.toSManga() } return MangasPage(mangaList, hasNextPage = false) } @@ -68,15 +63,8 @@ class OlympusScanlation : HttpSource() { } override fun latestUpdatesParse(response: Response): MangasPage { - runCatching { fetchFilters() } val result = json.decodeFromString(response.body.string()) - val mangaList = result.data.filter { it.type == "comic" }.map { - SManga.create().apply { - url = "/series/comic-${it.slug}" - title = it.name - thumbnail_url = it.cover - } - } + val mangaList = result.data.filter { it.type == "comic" }.map { it.toSManga() } val hasNextPage = result.current_page < result.last_page return MangasPage(mangaList, hasNextPage) } @@ -118,27 +106,14 @@ class OlympusScanlation : HttpSource() { } override fun searchMangaParse(response: Response): MangasPage { - runCatching { fetchFilters() } if (response.request.url.toString().startsWith("$apiBaseUrl/api/search")) { val result = json.decodeFromString(response.body.string()) - val mangaList = result.data.filter { it.type == "comic" }.map { - SManga.create().apply { - url = "/series/comic-${it.slug}" - title = it.name - thumbnail_url = it.cover - } - } + val mangaList = result.data.filter { it.type == "comic" }.map { it.toSManga() } return MangasPage(mangaList, hasNextPage = false) } val result = json.decodeFromString(response.body.string()) - val mangaList = result.data.series.data.map { - SManga.create().apply { - url = "/series/comic-${it.slug}" - title = it.name - thumbnail_url = it.cover - } - } + val mangaList = result.data.series.data.map { it.toSManga() } val hasNextPage = result.data.series.current_page < result.data.series.last_page return MangasPage(mangaList, hasNextPage) } @@ -152,14 +127,7 @@ class OlympusScanlation : HttpSource() { val newRequest = GET(url = apiUrl, headers = headers) val newResponse = client.newCall(newRequest).execute() val result = json.decodeFromString(newResponse.body.string()) - return SManga.create().apply { - url = "/series/comic-$slug" - title = result.data.name - thumbnail_url = result.data.cover - description = result.data.summary - status = parseStatus(result.data.status?.id) - genre = result.data.genres?.joinToString { it.name.trim() } - } + return result.data.toSMangaDetails() } override fun getChapterUrl(chapter: SChapter): String = baseUrl + chapter.url @@ -196,14 +164,7 @@ class OlympusScanlation : HttpSource() { resultSize += newData.data.size page += 1 } - return data.data.map { chap -> chapterFromObject(chap, slug) } - } - - private fun chapterFromObject(chapter: ChapterDto, slug: String) = SChapter.create().apply { - url = "/capitulo/${chapter.id}/comic-$slug" - name = "Capitulo ${chapter.name}" - date_upload = runCatching { dateFormat.parse(chapter.date)?.time } - .getOrNull() ?: 0L + return data.data.map { it.toSChapter(slug, dateFormat) } } override fun pageListRequest(chapter: SChapter): Request { @@ -220,20 +181,12 @@ class OlympusScanlation : HttpSource() { override fun pageListParse(response: Response): List { return json.decodeFromString(response.body.string()).chapter.pages.mapIndexed { i, img -> - Page(i, "", img) + Page(i, imageUrl = img) } } override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException() - private fun parseStatus(statusId: Int?) = when (statusId) { - 1 -> SManga.ONGOING - 3 -> SManga.ON_HIATUS - 4 -> SManga.COMPLETED - 5 -> SManga.CANCELLED - else -> SManga.UNKNOWN - } - private class SortFilter : Filter.Sort( "Ordenar", arrayOf("Alfabético"), @@ -257,13 +210,14 @@ class OlympusScanlation : HttpSource() { ) override fun getFilterList(): FilterList { + fetchFilters() val filters = mutableListOf>( Filter.Header("Los filtros no funcionan en la búsqueda por texto"), Filter.Separator(), SortFilter(), ) - if (genresList.isNotEmpty() || statusesList.isNotEmpty()) { + if (filtersState == FiltersState.FETCHED) { filters += listOf( Filter.Separator(), Filter.Header("Filtrar por género"), @@ -287,20 +241,25 @@ class OlympusScanlation : HttpSource() { private var genresList: List> = emptyList() private var statusesList: List> = emptyList() - private var fetchFiltersAttemps = 0 - private var fetchFiltersFailed = false + private var fetchFiltersAttempts = 0 + private var filtersState = FiltersState.NOT_FETCHED private fun fetchFilters() { - if (fetchFiltersAttemps <= 3 && ((genresList.isEmpty() && statusesList.isEmpty()) || fetchFiltersFailed)) { - val filters = runCatching { + if (filtersState != FiltersState.NOT_FETCHED || fetchFiltersAttempts >= 3) return + filtersState = FiltersState.FETCHING + fetchFiltersAttempts++ + thread { + try { val response = client.newCall(GET("$apiBaseUrl/api/genres-statuses", headers)).execute() - json.decodeFromString(response.body.string()) - } + val filters = json.decodeFromString(response.body.string()) - fetchFiltersFailed = filters.isFailure - genresList = filters.getOrNull()?.genres?.map { it.name.trim() to it.id } ?: emptyList() - statusesList = filters.getOrNull()?.statuses?.map { it.name.trim() to it.id } ?: emptyList() - fetchFiltersAttemps++ + genresList = filters.genres.map { it.name.trim() to it.id } + statusesList = filters.statuses.map { it.name.trim() to it.id } + + filtersState = FiltersState.FETCHED + } catch (e: Throwable) { + filtersState = FiltersState.NOT_FETCHED + } } } @@ -308,4 +267,6 @@ class OlympusScanlation : HttpSource() { Filter.Select(displayName, vals.map { it.first }.toTypedArray()) { fun toUriPart() = vals[state].second } + + private enum class FiltersState { NOT_FETCHED, FETCHING, FETCHED } } diff --git a/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlationDto.kt b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlationDto.kt index afb609bd9..08f1bbdd3 100644 --- a/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlationDto.kt +++ b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlationDto.kt @@ -1,93 +1,142 @@ package eu.kanade.tachiyomi.extension.es.olympusscanlation +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import java.text.ParseException +import java.text.SimpleDateFormat @Serializable -data class PayloadSeriesDto(val data: PayloadSeriesDataDto) - -@Serializable -data class PayloadSeriesDataDto( - val series: SeriesDto, - val recommended_series: String, +class PayloadHomeDto( + val data: HomeDto, ) @Serializable -data class SeriesDto( +class HomeDto( + @SerialName("popular_comics") val popularComics: String, +) + +@Serializable +class PayloadSeriesDto(val data: PayloadSeriesDataDto) + +@Serializable +class PayloadSeriesDataDto( + val series: SeriesDto, +) + +@Serializable +class SeriesDto( val current_page: Int, val data: List, val last_page: Int, ) @Serializable -data class PayloadMangaDto(val data: List) +class PayloadMangaDto(val data: List) @Serializable -data class MangaDto( - val id: Int, - val name: String, - val slug: String, - val cover: String? = null, +class MangaDto( + private val name: String, + private val slug: String, + private val cover: String? = null, val type: String? = null, - val summary: String? = null, - val status: MangaStatusDto? = null, - val genres: List? = null, -) + private val summary: String? = null, + private val status: MangaStatusDto? = null, + private val genres: List? = null, +) { + fun toSManga() = SManga.create().apply { + title = name + url = "/series/comic-$slug" + thumbnail_url = cover + } + + fun toSMangaDetails() = toSManga().apply { + description = summary + status = parseStatus() + genre = genres?.joinToString { it.name.trim() } + } + + private fun parseStatus(): Int { + val status = this.status ?: return SManga.UNKNOWN + return when (status.id) { + 1 -> SManga.ONGOING + 3 -> SManga.ON_HIATUS + 4 -> SManga.COMPLETED + 5 -> SManga.CANCELLED + else -> SManga.UNKNOWN + } + } +} @Serializable -data class NewChaptersDto( +class NewChaptersDto( val data: List, val current_page: Int, val last_page: Int, ) @Serializable -data class LatestMangaDto( - val id: Int, - val name: String, - val slug: String, - val cover: String? = null, +class LatestMangaDto( + private val name: String, + private val slug: String, + private val cover: String? = null, val type: String? = null, -) +) { + fun toSManga() = SManga.create().apply { + title = name + url = "/series/comic-$slug" + thumbnail_url = cover + } +} @Serializable -data class MangaDetailDto( +class MangaDetailDto( var data: MangaDto, ) @Serializable -data class PayloadChapterDto(var data: List, val meta: MetaDto) +class PayloadChapterDto(var data: List, val meta: MetaDto) @Serializable -data class ChapterDto( +class ChapterDto( + private val id: Int, + private val name: String, + @SerialName("published_at") private val date: String, +) { + fun toSChapter(mangaSlug: String, dateFormat: SimpleDateFormat) = SChapter.create().apply { + name = "Capitulo ${this@ChapterDto.name}" + url = "/capitulo/$id/comic-$mangaSlug" + date_upload = try { + dateFormat.parse(date)!!.time + } catch (e: ParseException) { + 0L + } + } +} + +@Serializable +class MetaDto(val total: Int) + +@Serializable +class PayloadPagesDto(val chapter: PageDto) + +@Serializable +class PageDto(val pages: List) + +@Serializable +class MangaStatusDto( val id: Int, - val name: String, - @SerialName("published_at") val date: String, ) @Serializable -data class MetaDto(val total: Int) - -@Serializable -data class PayloadPagesDto(val chapter: PageDto) - -@Serializable -data class PageDto(val pages: List) - -@Serializable -data class MangaStatusDto( - val id: Int, - val name: String, -) - -@Serializable -data class GenresStatusesDto( +class GenresStatusesDto( val genres: List, val statuses: List, ) @Serializable -data class FilterDto( +class FilterDto( val id: Int, val name: String, )