Refactor the code of some extensions. (#10313)

This commit is contained in:
Alessandro Jean 2022-01-04 07:44:45 -03:00 committed by GitHub
parent 5cb8df48af
commit 418cded260
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 147 additions and 142 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'Argos Scan' extName = 'Argos Scan'
pkgNameSuffix = 'pt.argosscan' pkgNameSuffix = 'pt.argosscan'
extClass = '.ArgosScan' extClass = '.ArgosScan'
extVersionCode = 18 extVersionCode = 19
} }
dependencies { dependencies {

View File

@ -16,7 +16,9 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.add import kotlinx.serialization.json.add
import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonArray
@ -83,14 +85,13 @@ class ArgosScan : HttpSource(), ConfigurableSource {
} }
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val result = json.parseToJsonElement(response.body!!.string()).jsonObject val result = response.parseAs<ArgosResponseDto<ArgosProjectListDto>>()
if (result["errors"] != null) { if (result.data == null) {
throw Exception(REQUEST_ERROR) throw Exception(REQUEST_ERROR)
} }
val projectList = result["data"]!!.jsonObject["getProjects"]!! val projectList = result.data["getProjects"]!!
.let { json.decodeFromJsonElement<ArgosProjectListDto>(it) }
val mangaList = projectList.projects val mangaList = projectList.projects
.map(::genericMangaFromObject) .map(::genericMangaFromObject)
@ -155,36 +156,32 @@ class ArgosScan : HttpSource(), ConfigurableSource {
} }
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply { override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
val result = json.parseToJsonElement(response.body!!.string()).jsonObject val result = response.parseAs<ArgosResponseDto<ArgosProjectDto>>()
if (result["errors"] != null) { if (result.data == null) {
throw Exception(REQUEST_ERROR) throw Exception(REQUEST_ERROR)
} }
val project = result["data"]!!.jsonObject["project"]!!.jsonObject val project = result.data["project"]!!
.let { json.decodeFromJsonElement<ArgosProjectDto>(it) }
title = project.name!! title = project.name!!
thumbnail_url = "$baseUrl/images/${project.id}/${project.cover!!}" thumbnail_url = "$baseUrl/images/${project.id}/${project.cover!!}"
description = project.description.orEmpty() description = project.description.orEmpty()
author = project.authors.orEmpty().joinToString(", ") author = project.authors.orEmpty().joinToString()
status = SManga.ONGOING status = SManga.ONGOING
genre = project.tags.orEmpty().joinToString(", ") { it.name } genre = project.tags.orEmpty().sortedBy(ArgosTagDto::name).joinToString { it.name }
} }
override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga) override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga)
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val result = json.parseToJsonElement(response.body!!.string()).jsonObject val result = response.parseAs<ArgosResponseDto<ArgosProjectDto>>()
if (result["errors"] != null) { if (result.data == null) {
throw Exception(REQUEST_ERROR) throw Exception(REQUEST_ERROR)
} }
val project = result["data"]!!.jsonObject["project"]!!.jsonObject return result.data["project"]!!.chapters.map(::chapterFromObject)
.let { json.decodeFromJsonElement<ArgosProjectDto>(it) }
return project.chapters.map(::chapterFromObject)
} }
private fun chapterFromObject(chapter: ArgosChapterDto): SChapter = SChapter.create().apply { private fun chapterFromObject(chapter: ArgosChapterDto): SChapter = SChapter.create().apply {
@ -211,7 +208,7 @@ class ArgosScan : HttpSource(), ConfigurableSource {
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val result = json.parseToJsonElement(response.body!!.string()).jsonObject val result = response.parseAs<JsonElement>().jsonObject
if (result["errors"] != null) { if (result["errors"] != null) {
throw Exception(REQUEST_ERROR) throw Exception(REQUEST_ERROR)
@ -345,6 +342,10 @@ class ArgosScan : HttpSource(), ConfigurableSource {
return POST(GRAPHQL_URL, newHeaders, body) return POST(GRAPHQL_URL, newHeaders, body)
} }
private inline fun <reified T> Response.parseAs(): T = use {
json.decodeFromString(it.body?.string().orEmpty())
}
private fun String.toDate(): Long { private fun String.toDate(): Long {
return runCatching { DATE_PARSER.parse(this)?.time } return runCatching { DATE_PARSER.parse(this)?.time }
.getOrNull() ?: 0L .getOrNull() ?: 0L

View File

@ -3,6 +3,11 @@ package eu.kanade.tachiyomi.extension.pt.argosscan
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable
data class ArgosResponseDto<T>(
val data: Map<String, T>? = null
)
@Serializable @Serializable
data class ArgosProjectListDto( data class ArgosProjectListDto(
val count: Int = 0, val count: Int = 0,

View File

@ -6,7 +6,7 @@ ext {
extName = 'HipercooL' extName = 'HipercooL'
pkgNameSuffix = 'pt.hipercool' pkgNameSuffix = 'pt.hipercool'
extClass = '.Hipercool' extClass = '.Hipercool'
extVersionCode = 8 extVersionCode = 9
isNsfw = true isNsfw = true
} }

View File

@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.extension.pt.hipercool package eu.kanade.tachiyomi.extension.pt.hipercool
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor import eu.kanade.tachiyomi.lib.ratelimit.SpecificHostRateLimitInterceptor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
@ -11,23 +11,21 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
class Hipercool : HttpSource() { class Hipercool : HttpSource() {
@ -43,7 +41,8 @@ class Hipercool : HttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(RateLimitInterceptor(1, 2, TimeUnit.SECONDS)) .addInterceptor(SpecificHostRateLimitInterceptor(baseUrl.toHttpUrl(), 1, 2))
.addInterceptor(SpecificHostRateLimitInterceptor(STATIC_URL.toHttpUrl(), 1, 1))
.build() .build()
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder(): Headers.Builder = Headers.Builder()
@ -54,14 +53,14 @@ class Hipercool : HttpSource() {
private val json: Json by injectLazy() private val json: Json by injectLazy()
private fun genericMangaListParse(response: Response): MangasPage { private fun genericMangaListParse(response: Response): MangasPage {
val chapters = json.decodeFromString<List<HipercoolChapterDto>>(response.body!!.string()) val chapters = response.parseAs<List<HipercoolChapterDto>>()
if (chapters.isEmpty()) if (chapters.isEmpty())
return MangasPage(emptyList(), false) return MangasPage(emptyList(), false)
val mangaList = chapters val mangaList = chapters
.distinctBy { it.book!!.title }
.map(::genericMangaFromObject) .map(::genericMangaFromObject)
.distinctBy { it.title }
val hasNextPage = chapters.size == DEFAULT_COUNT val hasNextPage = chapters.size == DEFAULT_COUNT
@ -87,17 +86,14 @@ class Hipercool : HttpSource() {
override fun latestUpdatesParse(response: Response): MangasPage = genericMangaListParse(response) override fun latestUpdatesParse(response: Response): MangasPage = genericMangaListParse(response)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val mediaType = "application/json; charset=utf-8".toMediaTypeOrNull() val searchPayload = HipercoolSearchDto(
start = (page - 1) * DEFAULT_COUNT,
count = DEFAULT_COUNT,
text = query,
type = "text"
)
// Create json body. val body = json.encodeToString(searchPayload).toRequestBody(JSON_MEDIA_TYPE)
val json = buildJsonObject {
put("start", (page - 1) * DEFAULT_COUNT)
put("content", DEFAULT_COUNT)
put("text", query)
put("type", "text")
}
val body = json.toString().toRequestBody(mediaType)
return POST("$baseUrl/api/books/chapters/search", headers, body) return POST("$baseUrl/api/books/chapters/search", headers, body)
} }
@ -119,39 +115,22 @@ class Hipercool : HttpSource() {
return GET("$baseUrl/api/books/$slug", headers) return GET("$baseUrl/api/books/$slug", headers)
} }
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
val book = json.decodeFromString<HipercoolBookDto>(response.body!!.string()) val book = response.parseAs<HipercoolBookDto>()
val artists = book.tags
.filter { it.label == "Artista" }
.flatMap { it.values }
.joinToString("; ") { it.label }
val authors = book.tags
.filter { it.label == "Autor" }
.flatMap { it.values }
.joinToString("; ") { it.label }
val tags = book.tags
.filter { it.label == "Tags" }
.flatMap { it.values }
.joinToString { it.label }
return SManga.create().apply {
title = book.title title = book.title
thumbnail_url = book.slug.toThumbnailUrl(book.revision) thumbnail_url = book.slug.toThumbnailUrl(book.revision)
description = book.synopsis.orEmpty() description = book.synopsis.orEmpty()
artist = artists artist = book.fixedTags["artista"].orEmpty().joinToString("; ")
author = authors author = book.fixedTags["autor"].orEmpty().joinToString("; ")
genre = tags genre = book.fixedTags["tags"].orEmpty().joinToString()
}
} }
// Chapters are available in the same url of the manga details. // Chapters are available in the same url of the manga details.
override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga) override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga)
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val book = json.decodeFromString<HipercoolBookDto>(response.body!!.string()) val book = response.parseAs<HipercoolBookDto>()
if (book.chapters is JsonPrimitive) if (book.chapters is JsonPrimitive)
return emptyList() return emptyList()
@ -166,8 +145,9 @@ class Hipercool : HttpSource() {
name = "Cap. " + chapter.title name = "Cap. " + chapter.title
chapter_number = chapter.title.toFloatOrNull() ?: -1f chapter_number = chapter.title.toFloatOrNull() ?: -1f
date_upload = chapter.publishedAt.toDate() date_upload = chapter.publishedAt.toDate()
scanlator = book.fixedTags["tradutor"]?.joinToString(" & ")
val fullUrl = "$baseUrl/books".toHttpUrlOrNull()!!.newBuilder() val fullUrl = "$baseUrl/books".toHttpUrl().newBuilder()
.addPathSegment(book.slug) .addPathSegment(book.slug)
.addPathSegment(chapter.slug) .addPathSegment(chapter.slug)
.addQueryParameter("images", chapter.images.toString()) .addQueryParameter("images", chapter.images.toString())
@ -183,30 +163,26 @@ class Hipercool : HttpSource() {
val bookSlug = chapterUrl.pathSegments[1] val bookSlug = chapterUrl.pathSegments[1]
val chapterSlug = chapterUrl.pathSegments[2] val chapterSlug = chapterUrl.pathSegments[2]
val images = chapterUrl.queryParameter("images")!!.toInt() val images = chapterUrl.queryParameter("images")!!.toInt()
val revision = chapterUrl.queryParameter("revision")!!.toInt() val revision = chapterUrl.queryParameter("revision")!!
val pages = arrayListOf<Page>() val pages = List(images) { i ->
val imageUrl = "$STATIC_URL/books".toHttpUrl().newBuilder()
// Create the pages.
for (i in 1..images) {
val imageUrl = "$STATIC_URL/books".toHttpUrlOrNull()!!.newBuilder()
.addPathSegment(bookSlug) .addPathSegment(bookSlug)
.addPathSegment(chapterSlug) .addPathSegment(chapterSlug)
.addPathSegment("$bookSlug-chapter-$chapterSlug-page-$i.jpg") .addPathSegment("$bookSlug-chapter-$chapterSlug-page-${i + 1}.jpg")
.addQueryParameter("revision", revision.toString()) .addQueryParameter("revision", revision)
.toString() .toString()
pages += Page(i - 1, chapter.url, imageUrl) Page(i, chapter.url, imageUrl)
} }
return Observable.just(pages) return Observable.just(pages)
} }
override fun pageListParse(response: Response): List<Page> = throw Exception("This method should not be called!") override fun pageListParse(response: Response): List<Page> =
throw Exception("This method should not be called!")
override fun fetchImageUrl(page: Page): Observable<String> { override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl!!)
return Observable.just(page.imageUrl!!)
}
override fun imageUrlParse(response: Response): String = "" override fun imageUrlParse(response: Response): String = ""
@ -218,12 +194,13 @@ class Hipercool : HttpSource() {
return GET(page.imageUrl!!, newHeaders) return GET(page.imageUrl!!, newHeaders)
} }
private fun String.toDate(): Long { private inline fun <reified T> Response.parseAs(): T = use {
return try { json.decodeFromString(it.body?.string().orEmpty())
DATE_FORMATTER.parse(substringBefore("T"))?.time ?: 0L
} catch (e: ParseException) {
0L
} }
private fun String.toDate(): Long {
return runCatching { DATE_FORMATTER.parse(this)?.time }
.getOrNull() ?: 0L
} }
private fun String.toThumbnailUrl(revision: Int): String = private fun String.toThumbnailUrl(revision: Int): String =
@ -236,11 +213,15 @@ class Hipercool : HttpSource() {
companion object { companion object {
private const val STATIC_URL = "https://static.hiper.cool" private const val STATIC_URL = "https://static.hiper.cool"
private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaType()
private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36" "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
private const val DEFAULT_COUNT = 40 private const val DEFAULT_COUNT = 40
private val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) } private val DATE_FORMATTER by lazy {
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH)
}
} }
} }

View File

@ -12,12 +12,18 @@ data class HipercoolBookDto(
val synopsis: String? = null, val synopsis: String? = null,
val tags: List<HipercoolTagDto> = emptyList(), val tags: List<HipercoolTagDto> = emptyList(),
val title: String val title: String
) ) {
val fixedTags: Map<String, List<String>>
get() = tags
.groupBy(HipercoolTagDto::slug, HipercoolTagDto::values)
.mapValues { it.value.flatten().map(HipercoolTagDto::label) }
}
@Serializable @Serializable
data class HipercoolTagDto( data class HipercoolTagDto(
val label: String, val label: String,
val values: List<HipercoolTagDto> = emptyList() val values: List<HipercoolTagDto> = emptyList(),
val slug: String
) )
@Serializable @Serializable
@ -28,3 +34,11 @@ data class HipercoolChapterDto(
val slug: String, val slug: String,
val title: String val title: String
) )
@Serializable
data class HipercoolSearchDto(
val start: Int,
val count: Int,
val text: String,
val type: String
)

View File

@ -6,7 +6,7 @@ ext {
extName = 'Saikai Scan' extName = 'Saikai Scan'
pkgNameSuffix = 'pt.saikaiscan' pkgNameSuffix = 'pt.saikaiscan'
extClass = '.SaikaiScan' extClass = '.SaikaiScan'
extVersionCode = 7 extVersionCode = 8
} }
dependencies { dependencies {

View File

@ -1,9 +1,8 @@
package eu.kanade.tachiyomi.extension.pt.saikaiscan package eu.kanade.tachiyomi.extension.pt.saikaiscan
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor import eu.kanade.tachiyomi.lib.ratelimit.SpecificHostRateLimitInterceptor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
@ -20,10 +19,8 @@ import okhttp3.Response
import org.jsoup.Jsoup import org.jsoup.Jsoup
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
class SaikaiScan : HttpSource() { class SaikaiScan : HttpSource() {
@ -36,7 +33,8 @@ class SaikaiScan : HttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(RateLimitInterceptor(1, 2, TimeUnit.SECONDS)) .addInterceptor(SpecificHostRateLimitInterceptor(API_URL.toHttpUrl(), 1, 2))
.addInterceptor(SpecificHostRateLimitInterceptor(IMAGE_SERVER_URL.toHttpUrl(), 1, 1))
.build() .build()
private val json: Json by injectLazy() private val json: Json by injectLazy()
@ -63,7 +61,7 @@ class SaikaiScan : HttpSource() {
} }
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val result = json.decodeFromString<SaikaiScanPaginatedStoriesDto>(response.body!!.string()) val result = response.parseAs<SaikaiScanPaginatedStoriesDto>()
val mangaList = result.data!!.map(::popularMangaFromObject) val mangaList = result.data!!.map(::popularMangaFromObject)
val hasNextPage = result.meta!!.currentPage < result.meta.lastPage val hasNextPage = result.meta!!.currentPage < result.meta.lastPage
@ -170,7 +168,7 @@ class SaikaiScan : HttpSource() {
} }
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply { override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
val result = json.decodeFromString<SaikaiScanPaginatedStoriesDto>(response.body!!.string()) val result = response.parseAs<SaikaiScanPaginatedStoriesDto>()
val story = result.data!![0] val story = result.data!![0]
title = story.title title = story.title
@ -202,13 +200,13 @@ class SaikaiScan : HttpSource() {
} }
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val result = json.decodeFromString<SaikaiScanPaginatedStoriesDto>(response.body!!.string()) val result = response.parseAs<SaikaiScanPaginatedStoriesDto>()
val story = result.data!![0] val story = result.data!![0]
return story.releases return story.releases
.filter { it.isActive == 1 } .filter { it.isActive == 1 }
.map { chapterFromObject(it, story.slug) } .map { chapterFromObject(it, story.slug) }
.sortedByDescending { it.chapter_number } .sortedByDescending(SChapter::chapter_number)
} }
private fun chapterFromObject(obj: SaikaiScanReleaseDto, storySlug: String): SChapter = private fun chapterFromObject(obj: SaikaiScanReleaseDto, storySlug: String): SChapter =
@ -216,7 +214,7 @@ class SaikaiScan : HttpSource() {
name = "Capítulo ${obj.chapter}" + name = "Capítulo ${obj.chapter}" +
(if (obj.title.isNullOrEmpty().not()) " - ${obj.title}" else "") (if (obj.title.isNullOrEmpty().not()) " - ${obj.title}" else "")
chapter_number = obj.chapter.toFloatOrNull() ?: -1f chapter_number = obj.chapter.toFloatOrNull() ?: -1f
date_upload = obj.publishedAt.substringBefore(" ").toDate() date_upload = obj.publishedAt.toDate()
scanlator = this@SaikaiScan.name scanlator = this@SaikaiScan.name
url = "/ler/comics/$storySlug/${obj.id}/${obj.slug}" url = "/ler/comics/$storySlug/${obj.id}/${obj.slug}"
} }
@ -238,7 +236,7 @@ class SaikaiScan : HttpSource() {
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val result = json.decodeFromString<SaikaiScanReleaseResultDto>(response.body!!.string()) val result = response.parseAs<SaikaiScanReleaseResultDto>()
return result.data!!.releaseImages.mapIndexed { i, obj -> return result.data!!.releaseImages.mapIndexed { i, obj ->
Page(i, "", "$IMAGE_SERVER_URL/${obj.image}") Page(i, "", "$IMAGE_SERVER_URL/${obj.image}")
@ -257,43 +255,6 @@ class SaikaiScan : HttpSource() {
return GET(page.imageUrl!!, imageHeaders) return GET(page.imageUrl!!, imageHeaders)
} }
private class Genre(title: String, val id: Int) : Filter.CheckBox(title)
private class GenreFilter(genres: List<Genre>) : Filter.Group<Genre>("Gêneros", genres)
private data class Country(val name: String, val id: Int) {
override fun toString(): String = name
}
private open class EnhancedSelect<T>(name: String, values: Array<T>) : Filter.Select<T>(name, values) {
val selected: T
get() = values[state]
}
private class CountryFilter(countries: List<Country>) : EnhancedSelect<Country>(
"Nacionalidade",
countries.toTypedArray()
)
private data class Status(val name: String, val id: Int) {
override fun toString(): String = name
}
private class StatusFilter(statuses: List<Status>) : EnhancedSelect<Status>(
"Status",
statuses.toTypedArray()
)
private data class SortProperty(val name: String, val slug: String) {
override fun toString(): String = name
}
private class SortByFilter(val sortProperties: List<SortProperty>) : Filter.Sort(
"Ordenar por",
sortProperties.map { it.name }.toTypedArray(),
Selection(2, ascending = false)
)
// fetch('https://api.saikai.com.br/api/genres') // fetch('https://api.saikai.com.br/api/genres')
// .then(res => res.json()) // .then(res => res.json())
// .then(res => console.log(res.data.map(g => `Genre("${g.name}", ${g.id})`).join(',\n'))) // .then(res => console.log(res.data.map(g => `Genre("${g.name}", ${g.id})`).join(',\n')))
@ -382,12 +343,13 @@ class SaikaiScan : HttpSource() {
GenreFilter(getGenreList()) GenreFilter(getGenreList())
) )
private fun String.toDate(): Long { private inline fun <reified T> Response.parseAs(): T = use {
return try { json.decodeFromString(it.body?.string().orEmpty())
DATE_FORMATTER.parse(this)?.time ?: 0L
} catch (e: ParseException) {
0L
} }
private fun String.toDate(): Long {
return runCatching { DATE_FORMATTER.parse(this)?.time }
.getOrNull() ?: 0L
} }
private fun String.toStatus(): Int = when (this) { private fun String.toStatus(): Int = when (this) {
@ -403,7 +365,9 @@ class SaikaiScan : HttpSource() {
private const val COMIC_FORMAT_ID = "2" private const val COMIC_FORMAT_ID = "2"
private const val PER_PAGE = "12" private const val PER_PAGE = "12"
private val DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd", Locale("pt", "BR")) 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 API_URL = "https://api.saikai.com.br"
private const val IMAGE_SERVER_URL = "https://s3-alpha.saikai.com.br" private const val IMAGE_SERVER_URL = "https://s3-alpha.saikai.com.br"

View File

@ -0,0 +1,40 @@
package eu.kanade.tachiyomi.extension.pt.saikaiscan
import eu.kanade.tachiyomi.source.model.Filter
class Genre(title: String, val id: Int) : Filter.CheckBox(title)
class GenreFilter(genres: List<Genre>) : Filter.Group<Genre>("Gêneros", genres)
data class Country(val name: String, val id: Int) {
override fun toString(): String = name
}
open class EnhancedSelect<T>(name: String, values: Array<T>) : Filter.Select<T>(name, values) {
val selected: T
get() = values[state]
}
class CountryFilter(countries: List<Country>) : EnhancedSelect<Country>(
"Nacionalidade",
countries.toTypedArray()
)
data class Status(val name: String, val id: Int) {
override fun toString(): String = name
}
class StatusFilter(statuses: List<Status>) : EnhancedSelect<Status>(
"Status",
statuses.toTypedArray()
)
data class SortProperty(val name: String, val slug: String) {
override fun toString(): String = name
}
class SortByFilter(val sortProperties: List<SortProperty>) : Filter.Sort(
"Ordenar por",
sortProperties.map { it.name }.toTypedArray(),
Selection(2, ascending = false)
)