Koinobori Scan: Update to custom theme (#5097)
* first attempt * termux my beloved
This commit is contained in:
parent
66eb921df6
commit
5aa0cd2afa
|
@ -1,9 +1,7 @@
|
|||
ext {
|
||||
extName = 'Koinobori Scan'
|
||||
extClass = '.KoinoboriScan'
|
||||
themePkg = 'madara'
|
||||
baseUrl = 'https://koinoboriscan.com'
|
||||
overrideVersionCode = 0
|
||||
extVersionCode = 37
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,162 @@
|
|||
package eu.kanade.tachiyomi.extension.es.koinoboriscan
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
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.json.Json
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import kotlin.math.min
|
||||
|
||||
class KoinoboriScan : Madara(
|
||||
"Koinobori Scan",
|
||||
"https://koinoboriscan.com",
|
||||
"es",
|
||||
SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
||||
) {
|
||||
override val client = super.client.newBuilder()
|
||||
class KoinoboriScan : HttpSource() {
|
||||
|
||||
// Site change theme from Madara to custom
|
||||
override val versionId = 2
|
||||
|
||||
override val name = "Koinobori Scan"
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
override val baseUrl = "https://visorkoi.com"
|
||||
|
||||
private val apiBaseUrl = "https://api.visorkoi.com"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale("es"))
|
||||
|
||||
override val client = network.cloudflareClient.newBuilder()
|
||||
.rateLimit(2, 1)
|
||||
.build()
|
||||
|
||||
override fun headersBuilder() = super.headersBuilder()
|
||||
.set("Referer", "$baseUrl/")
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request =
|
||||
GET("$apiBaseUrl/topSeries", headers)
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val mangas = json.decodeFromString<List<SeriesDto>>(response.body.string())
|
||||
.map { it.toSManga(apiBaseUrl) }
|
||||
|
||||
return MangasPage(mangas, false)
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request =
|
||||
GET("$apiBaseUrl/lastupdates", headers)
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val mangas = json.decodeFromString<List<SeriesDto>>(response.body.string())
|
||||
.map { it.toSManga(apiBaseUrl) }
|
||||
|
||||
return MangasPage(mangas, false)
|
||||
}
|
||||
|
||||
private val seriesList = mutableListOf<SeriesDto>()
|
||||
|
||||
override fun fetchSearchManga(
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList,
|
||||
): Observable<MangasPage> {
|
||||
return if (seriesList.isEmpty()) {
|
||||
client.newCall(searchMangaRequest(page, query, filters))
|
||||
.asObservableSuccess()
|
||||
.map { searchMangaParse(it, page, query) }
|
||||
} else {
|
||||
Observable.just(parseSeriesList(page, query))
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
|
||||
GET("$apiBaseUrl/all", headers)
|
||||
|
||||
private fun searchMangaParse(response: Response, page: Int, query: String): MangasPage {
|
||||
val result = json.decodeFromString<List<SeriesDto>>(response.body.string())
|
||||
seriesList.addAll(result)
|
||||
return parseSeriesList(page, query)
|
||||
}
|
||||
|
||||
private fun parseSeriesList(page: Int, query: String): MangasPage {
|
||||
val filteredSeries = seriesList.filter {
|
||||
it.title.contains(query, ignoreCase = true)
|
||||
}
|
||||
|
||||
val mangas = filteredSeries.subList(
|
||||
(page - 1) * SERIES_PER_PAGE,
|
||||
min(page * SERIES_PER_PAGE, filteredSeries.size),
|
||||
).map { it.toSManga(apiBaseUrl) }
|
||||
|
||||
val hasNextPage = filteredSeries.size > page * SERIES_PER_PAGE
|
||||
|
||||
return MangasPage(mangas, hasNextPage)
|
||||
}
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
Filter.Header("Presione 'Filtrar' para mostrar toda la biblioteca"),
|
||||
)
|
||||
|
||||
override fun getMangaUrl(manga: SManga) = "$baseUrl/?tipo=serie&identificador=${manga.url}"
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request =
|
||||
GET("$apiBaseUrl/api/project/${manga.url}", headers)
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga {
|
||||
return json.decodeFromString<SeriesDto>(response.body.string()).toSMangaDetails(apiBaseUrl)
|
||||
}
|
||||
|
||||
override fun getChapterUrl(chapter: SChapter) = "$baseUrl/?tipo=capitulo&identificador=${chapter.url}"
|
||||
|
||||
override fun chapterListRequest(manga: SManga): Request =
|
||||
GET("$apiBaseUrl/api/project/${manga.url}", headers)
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val result = json.decodeFromString<ChaptersPayloadDto>(response.body.string())
|
||||
return result.seasons.flatMap { season ->
|
||||
season.chapters.map { chapter ->
|
||||
SChapter.create().apply {
|
||||
url = chapter.id.toString()
|
||||
name = "Capítulo ${chapter.name}: ${chapter.title}"
|
||||
date_upload = try {
|
||||
dateFormat.parse(chapter.date)?.time ?: 0
|
||||
} catch (e: Exception) {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}.reversed()
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request =
|
||||
GET("$apiBaseUrl/api/chapter/${chapter.url}", headers)
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val result = json.decodeFromString<PagesPayloadDto>(response.body.string())
|
||||
val key = result.key
|
||||
val chapterId = result.chapter.id
|
||||
return result.chapter.images.mapIndexed { i, img ->
|
||||
Page(i, imageUrl = "$apiBaseUrl/api/images/chapter/$chapterId/$img?token=$key")
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response) = throw UnsupportedOperationException()
|
||||
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
|
||||
|
||||
companion object {
|
||||
const val SERIES_PER_PAGE = 24
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
package eu.kanade.tachiyomi.extension.es.koinoboriscan
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
class SeriesDto(
|
||||
@SerialName("ID") private val id: Int,
|
||||
val title: String,
|
||||
private val description: String,
|
||||
private val thumbnail: String,
|
||||
private val status: String?,
|
||||
private val author: String?,
|
||||
private val tags: List<SeriesTagsDto>? = emptyList(),
|
||||
) {
|
||||
fun toSManga(cdnUrl: String) = SManga.create().apply {
|
||||
title = this@SeriesDto.title
|
||||
thumbnail_url = cdnUrl + thumbnail
|
||||
url = id.toString()
|
||||
}
|
||||
|
||||
fun toSMangaDetails(cdnUrl: String) = SManga.create().apply {
|
||||
title = this@SeriesDto.title.trim()
|
||||
author = this@SeriesDto.author?.trim()
|
||||
status = parseStatus(this@SeriesDto.status)
|
||||
thumbnail_url = cdnUrl + thumbnail
|
||||
genre = tags?.joinToString { it.name.trim() }
|
||||
description = this@SeriesDto.description.trim()
|
||||
}
|
||||
|
||||
private fun parseStatus(status: String?) = when (status?.trim()) {
|
||||
"En emisión", "En curso" -> SManga.ONGOING
|
||||
"Completado" -> SManga.COMPLETED
|
||||
"Abandonado" -> SManga.CANCELLED
|
||||
"Pausado" -> SManga.ON_HIATUS
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class SeriesTagsDto(
|
||||
val name: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class ChaptersPayloadDto(
|
||||
val seasons: List<SeasonDto>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class SeasonDto(
|
||||
val chapters: List<ChapterDto>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class ChapterDto(
|
||||
@SerialName("ID") val id: Int,
|
||||
@SerialName("chapter_name") val name: String,
|
||||
@SerialName("chapter_title") val title: String,
|
||||
@SerialName("CreatedAt") val date: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class PagesPayloadDto(
|
||||
val chapter: ChapterImagesDto,
|
||||
val key: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class ChapterImagesDto(
|
||||
@SerialName("ID") val id: Int,
|
||||
val images: List<String>,
|
||||
)
|
Loading…
Reference in New Issue