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 {
|
ext {
|
||||||
extName = 'Koinobori Scan'
|
extName = 'Koinobori Scan'
|
||||||
extClass = '.KoinoboriScan'
|
extClass = '.KoinoboriScan'
|
||||||
themePkg = 'madara'
|
extVersionCode = 37
|
||||||
baseUrl = 'https://koinoboriscan.com'
|
|
||||||
overrideVersionCode = 0
|
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,162 @@
|
||||||
package eu.kanade.tachiyomi.extension.es.koinoboriscan
|
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.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.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
class KoinoboriScan : Madara(
|
class KoinoboriScan : HttpSource() {
|
||||||
"Koinobori Scan",
|
|
||||||
"https://koinoboriscan.com",
|
// Site change theme from Madara to custom
|
||||||
"es",
|
override val versionId = 2
|
||||||
SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
|
||||||
) {
|
override val name = "Koinobori Scan"
|
||||||
override val client = super.client.newBuilder()
|
|
||||||
|
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)
|
.rateLimit(2, 1)
|
||||||
.build()
|
.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