Add new source "MangaTigre" (#16444)
* Add MangaTigre * Keep title * Add more genres to manga * Use StringBuilder for create description * Minor changes
This commit is contained in:
parent
57c5e0c896
commit
3091cd6fdc
2
src/es/mangatigre/AndroidManifest.xml
Normal file
2
src/es/mangatigre/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="eu.kanade.tachiyomi.extension" />
|
13
src/es/mangatigre/build.gradle
Normal file
13
src/es/mangatigre/build.gradle
Normal file
@ -0,0 +1,13 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'MangaTigre'
|
||||
pkgNameSuffix = 'es.mangatigre'
|
||||
extClass = '.MangaTigre'
|
||||
extVersionCode = 1
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
BIN
src/es/mangatigre/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/es/mangatigre/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
src/es/mangatigre/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/es/mangatigre/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
BIN
src/es/mangatigre/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/es/mangatigre/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
BIN
src/es/mangatigre/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/es/mangatigre/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
src/es/mangatigre/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/es/mangatigre/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
src/es/mangatigre/res/web_hi_res_512.png
Normal file
BIN
src/es/mangatigre/res/web_hi_res_512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 122 KiB |
@ -0,0 +1,384 @@
|
||||
package eu.kanade.tachiyomi.extension.es.mangatigre
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||
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 eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import okio.Buffer
|
||||
import org.jsoup.nodes.Document
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.Calendar
|
||||
|
||||
class MangaTigre : HttpSource() {
|
||||
|
||||
override val name = "MangaTigre"
|
||||
|
||||
override val baseUrl = "https://www.mangatigre.net"
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val imgCDNUrl = "https://i2.mtcdn.xyz"
|
||||
|
||||
private var mtToken = ""
|
||||
|
||||
override val client: OkHttpClient = network.client.newBuilder()
|
||||
.addInterceptor { chain ->
|
||||
val request = chain.request()
|
||||
|
||||
if (request.method == "POST") {
|
||||
val response = chain.proceed(request)
|
||||
|
||||
if (response.code == 419) {
|
||||
response.close()
|
||||
getToken()
|
||||
|
||||
val newBody = json.parseToJsonElement(request.bodyString).jsonObject.toMutableMap().apply {
|
||||
this["_token"] = JsonPrimitive(mtToken)
|
||||
}
|
||||
|
||||
val payload = Json.encodeToString(JsonObject(newBody)).toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val apiHeaders = headersBuilder()
|
||||
.add("Accept", ACCEPT_JSON)
|
||||
.add("Content-Type", payload.contentType().toString())
|
||||
.build()
|
||||
|
||||
val newRequest = request.newBuilder()
|
||||
.headers(apiHeaders)
|
||||
.method(request.method, payload)
|
||||
.build()
|
||||
|
||||
return@addInterceptor chain.proceed(newRequest)
|
||||
}
|
||||
return@addInterceptor response
|
||||
}
|
||||
chain.proceed(request)
|
||||
}
|
||||
.rateLimitHost(baseUrl.toHttpUrl(), 1, 2)
|
||||
.build()
|
||||
|
||||
private fun getToken() {
|
||||
val document = client.newCall(GET(baseUrl, headers)).execute().asJsoup()
|
||||
mtToken = document.selectFirst("input.input-search[data-csrf]")!!.attr("data-csrf")
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
val payloadObj = PayloadManga(
|
||||
page = page,
|
||||
token = mtToken,
|
||||
)
|
||||
|
||||
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val apiHeaders = headersBuilder()
|
||||
.add("Accept", ACCEPT_JSON)
|
||||
.add("Content-Type", payload.contentType().toString())
|
||||
.build()
|
||||
|
||||
return POST("$baseUrl/mangas?sort=views", apiHeaders, payload)
|
||||
}
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val jsonString = response.body.string()
|
||||
|
||||
val result = json.decodeFromString<MangasDto>(jsonString)
|
||||
|
||||
val mangas = result.mangas.map {
|
||||
SManga.create().apply {
|
||||
setUrlWithoutDomain("$baseUrl/manga/${it.slug}")
|
||||
title = it.title
|
||||
thumbnail_url = "$imgCDNUrl/mangas/${it.thumbnailFileName}"
|
||||
}
|
||||
}
|
||||
val hasNextPage = result.totalPages > result.page
|
||||
|
||||
return MangasPage(mangas, hasNextPage)
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request {
|
||||
val payloadObj = PayloadManga(
|
||||
page = page,
|
||||
token = mtToken,
|
||||
)
|
||||
|
||||
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val apiHeaders = headersBuilder()
|
||||
.add("Accept", ACCEPT_JSON)
|
||||
.add("Content-Type", payload.contentType().toString())
|
||||
.build()
|
||||
|
||||
return POST("$baseUrl/mangas?sort=date", apiHeaders, payload)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
if (query.isNotEmpty()) {
|
||||
if (query.length < 2) throw Exception("La cadena de búsqueda debe tener por lo menos 2 caracteres")
|
||||
|
||||
val payloadObj = PayloadSearch(
|
||||
query = query,
|
||||
token = mtToken,
|
||||
)
|
||||
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val apiHeaders = headersBuilder()
|
||||
.add("Accept", ACCEPT_JSON)
|
||||
.add("Content-Type", payload.contentType().toString())
|
||||
.build()
|
||||
|
||||
return POST("$baseUrl/mangas/search#$query", apiHeaders, payload)
|
||||
}
|
||||
|
||||
val url = "$baseUrl/mangas".toHttpUrlOrNull()!!.newBuilder()
|
||||
|
||||
filters.forEach { filter ->
|
||||
when (filter) {
|
||||
is OrderFilter -> {
|
||||
url.addQueryParameter("sort", filter.toUriPart())
|
||||
}
|
||||
is TypeFilter -> {
|
||||
filter.state.forEach { content ->
|
||||
if (content.state) url.addQueryParameter("type[]", content.id)
|
||||
}
|
||||
}
|
||||
is StatusFilter -> {
|
||||
filter.state.forEach { content ->
|
||||
if (content.state) url.addQueryParameter("status[]", content.id)
|
||||
}
|
||||
}
|
||||
is DemographicFilter -> {
|
||||
filter.state.forEach { content ->
|
||||
if (content.state) url.addQueryParameter("demographic[]", content.id)
|
||||
}
|
||||
}
|
||||
is ContentFilter -> {
|
||||
filter.state.forEach { content ->
|
||||
if (content.state) url.addQueryParameter("content[]", content.id)
|
||||
}
|
||||
}
|
||||
is FormatFilter -> {
|
||||
filter.state.forEach { content ->
|
||||
if (content.state) url.addQueryParameter("format[]", content.id)
|
||||
}
|
||||
}
|
||||
is GenreFilter -> {
|
||||
filter.state.forEach { content ->
|
||||
if (content.state) url.addQueryParameter("genre[]", content.id)
|
||||
}
|
||||
}
|
||||
is ThemeFilter -> {
|
||||
filter.state.forEach { content ->
|
||||
if (content.state) url.addQueryParameter("theme[]", content.id)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
val payloadObj = PayloadManga(
|
||||
page = page,
|
||||
token = mtToken,
|
||||
)
|
||||
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val apiHeaders = headersBuilder()
|
||||
.add("Accept", ACCEPT_JSON)
|
||||
.add("Content-Type", payload.contentType().toString())
|
||||
.build()
|
||||
|
||||
return POST(url.build().toString(), apiHeaders, payload)
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val query = response.request.url.fragment
|
||||
val jsonString = response.body.string()
|
||||
|
||||
if (!query.isNullOrEmpty()) {
|
||||
val result = json.decodeFromString<SearchDto>(jsonString)
|
||||
|
||||
val mangas = result.result.map {
|
||||
SManga.create().apply {
|
||||
setUrlWithoutDomain("$baseUrl/manga/${it.slug}")
|
||||
title = it.title
|
||||
thumbnail_url = "$imgCDNUrl/mangas/${it.thumbnailFileName}"
|
||||
}
|
||||
}
|
||||
|
||||
return MangasPage(mangas, false)
|
||||
}
|
||||
|
||||
val result = json.decodeFromString<MangasDto>(jsonString)
|
||||
|
||||
val mangas = result.mangas.map {
|
||||
SManga.create().apply {
|
||||
setUrlWithoutDomain("$baseUrl/manga/${it.slug}")
|
||||
title = it.title
|
||||
thumbnail_url = "$imgCDNUrl/mangas/${it.thumbnailFileName}"
|
||||
}
|
||||
}
|
||||
val hasNextPage = result.totalPages > result.page
|
||||
|
||||
return MangasPage(mangas, hasNextPage)
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga {
|
||||
val document = response.asJsoup()
|
||||
return SManga.create().apply {
|
||||
description = createDescription(document)
|
||||
genre = createGenres(document)
|
||||
thumbnail_url = document.selectFirst("div.manga-image > img")!!.attr("abs:data-src")
|
||||
author = document.selectFirst("li.list-group-item:has(strong:contains(Autor)) > a")?.ownText()?.trim()
|
||||
artist = document.selectFirst("li.list-group-item:has(strong:contains(Artista)) > a")?.ownText()?.trim()
|
||||
status = document.selectFirst("li.list-group-item:has(strong:contains(Estado))")?.ownText()?.trim()!!.toStatus()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createGenres(document: Document): String {
|
||||
val demographic = document.select("li.list-group-item:has(strong:contains(Demografía)) a").joinToString { it.text() }
|
||||
val genres = document.select("li.list-group-item:has(strong:contains(Géneros)) a").joinToString { it.text() }
|
||||
val themes = document.select("li.list-group-item:has(strong:contains(Temas)) a").joinToString { it.text() }
|
||||
val content = document.select("li.list-group-item:has(strong:contains(Contenido)) a").joinToString { it.text() }
|
||||
return listOf(demographic, genres, themes, content).joinToString(", ")
|
||||
}
|
||||
|
||||
private fun createDescription(document: Document): String {
|
||||
val originalName = document.selectFirst("li.list-group-item:has(strong:contains(Original))")?.ownText()?.trim() ?: ""
|
||||
val alternativeName = document.select("li.list-group-item:has(strong:contains(Alternativo)) span.alter-name").text()
|
||||
val year = document.selectFirst("li.list-group-item:has(strong:contains(Año))")?.ownText()?.trim() ?: ""
|
||||
val animeAdaptation = document.selectFirst("li.list-group-item:has(strong:contains(Anime))")?.ownText()?.trim() ?: ""
|
||||
val country = document.selectFirst("li.list-group-item:has(strong:contains(País))")?.ownText()?.trim() ?: ""
|
||||
val summary = document.selectFirst("div.synopsis > p")?.ownText()?.trim() ?: ""
|
||||
return StringBuilder()
|
||||
.appendLine("Nombre Original: $originalName")
|
||||
.appendLine("Títulos Alternativos: $alternativeName")
|
||||
.appendLine("Año: $year")
|
||||
.appendLine("Adaptación al Anime: $animeAdaptation")
|
||||
.appendLine("País: $country")
|
||||
.appendLine()
|
||||
.appendLine("Sinopsis: $summary")
|
||||
.toString()
|
||||
}
|
||||
|
||||
override fun chapterListRequest(manga: SManga): Request {
|
||||
val payloadObj = PayloadChapter(
|
||||
token = mtToken,
|
||||
)
|
||||
|
||||
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val apiHeaders = headersBuilder()
|
||||
.add("Accept", ACCEPT_JSON)
|
||||
.add("Content-Type", payload.contentType().toString())
|
||||
.build()
|
||||
|
||||
return POST("$baseUrl${manga.url}", apiHeaders, payload)
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
return response.asJsoup().select("li").map {
|
||||
SChapter.create().apply {
|
||||
setUrlWithoutDomain(it.select("a").attr("href"))
|
||||
name = it.selectFirst("a")!!.ownText().trim()
|
||||
date_upload = parseRelativeDate(it.selectFirst("span")!!.ownText().trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val document = response.asJsoup()
|
||||
val script = document.selectFirst("script:containsData(window.chapter)")!!.data()
|
||||
val jsonString = CHAPTERS_REGEX.find(script)!!.groupValues[1]
|
||||
|
||||
val result = json.decodeFromString<ChapterDto>(jsonString)
|
||||
val slug = result.manga.slug
|
||||
val number = result.number
|
||||
|
||||
return result.images.map {
|
||||
val imageUrl = "$imgCDNUrl/chapters/$slug/$number/${it.value.name}.${it.value.format}"
|
||||
Page(it.key.toInt(), "", imageUrl)
|
||||
}.sortedBy { it.index }
|
||||
}
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
Filter.Header("Los filtros serán ignorados si se realiza una búsqueda textual"),
|
||||
Filter.Separator(),
|
||||
OrderFilter(),
|
||||
Filter.Separator(),
|
||||
TypeFilter(getFilterTypeList()),
|
||||
Filter.Separator(),
|
||||
StatusFilter(getFilterStatusList()),
|
||||
Filter.Separator(),
|
||||
DemographicFilter(getFilterDemographicList()),
|
||||
Filter.Separator(),
|
||||
ContentFilter(getFilterContentList()),
|
||||
Filter.Separator(),
|
||||
FormatFilter(getFilterFormatList()),
|
||||
Filter.Separator(),
|
||||
GenreFilter(getFilterGenreList()),
|
||||
Filter.Separator(),
|
||||
ThemeFilter(getFilterThemeList()),
|
||||
)
|
||||
|
||||
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not used.")
|
||||
|
||||
private fun parseRelativeDate(date: String): Long {
|
||||
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
|
||||
val cal = Calendar.getInstance()
|
||||
|
||||
return when {
|
||||
WordSet("segundo").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
|
||||
WordSet("minuto").anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
|
||||
WordSet("hora").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
|
||||
WordSet("día").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
|
||||
WordSet("semana").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number * 7) }.timeInMillis
|
||||
WordSet("mes").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
|
||||
WordSet("año").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
class WordSet(private vararg val words: String) {
|
||||
fun anyWordIn(dateString: String): Boolean = words.any { dateString.contains(it, ignoreCase = true) }
|
||||
}
|
||||
|
||||
private val Request.bodyString: String
|
||||
get() {
|
||||
val requestCopy = newBuilder().build()
|
||||
val buffer = Buffer()
|
||||
|
||||
return runCatching { buffer.apply { requestCopy.body!!.writeTo(this) }.readUtf8() }
|
||||
.getOrNull() ?: ""
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ACCEPT_JSON = "application/json, text/plain, */*"
|
||||
private val JSON_MEDIA_TYPE = "application/json".toMediaType()
|
||||
|
||||
private val CHAPTERS_REGEX = """window\.chapter\s*=\s*'(.+?)';""".toRegex()
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package eu.kanade.tachiyomi.extension.es.mangatigre
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PayloadManga(
|
||||
val page: Int,
|
||||
@SerialName("_token") val token: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PayloadChapter(
|
||||
@SerialName("_token") val token: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PayloadSearch(
|
||||
val query: String,
|
||||
@SerialName("_token") val token: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangasDto(
|
||||
@SerialName("current_page") val page: Int,
|
||||
@SerialName("last_page") val totalPages: Int,
|
||||
@SerialName("data") val mangas: List<MangasDataDto>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangasDataDto(
|
||||
@SerialName("name") val title: String,
|
||||
val slug: String,
|
||||
@SerialName("image") val thumbnailFileName: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ChapterDto(
|
||||
val manga: ChapterMangaInfoDto,
|
||||
val number: Int,
|
||||
val images: Map<String, ChapterImagesDto>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ChapterMangaInfoDto(
|
||||
val slug: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ChapterImagesDto(
|
||||
val name: String,
|
||||
val format: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SearchDto(
|
||||
val result: List<SearchDataDto>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SearchDataDto(
|
||||
val id: Int,
|
||||
@SerialName("name") val title: String,
|
||||
val slug: String,
|
||||
@SerialName("image")val thumbnailFileName: String,
|
||||
)
|
||||
|
||||
fun String.toStatus(): Int = when (this) {
|
||||
"En Marcha" -> SManga.ONGOING
|
||||
"Terminado" -> SManga.COMPLETED
|
||||
"Detenido" -> SManga.ON_HIATUS
|
||||
"Pausado" -> SManga.ON_HIATUS
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
package eu.kanade.tachiyomi.extension.es.mangatigre
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
|
||||
class Type(name: String, val id: String) : Filter.CheckBox(name)
|
||||
class TypeFilter(values: List<Type>) : Filter.Group<Type>("Tipos", values)
|
||||
|
||||
class Status(name: String, val id: String) : Filter.CheckBox(name)
|
||||
class StatusFilter(values: List<Status>) : Filter.Group<Status>("Estado", values)
|
||||
|
||||
class Demographic(name: String, val id: String) : Filter.CheckBox(name)
|
||||
class DemographicFilter(values: List<Demographic>) : Filter.Group<Demographic>("Demografía", values)
|
||||
|
||||
class Content(name: String, val id: String) : Filter.CheckBox(name)
|
||||
class ContentFilter(values: List<Content>) : Filter.Group<Content>("Contenido", values)
|
||||
|
||||
class Format(name: String, val id: String) : Filter.CheckBox(name)
|
||||
class FormatFilter(values: List<Format>) : Filter.Group<Format>("Formato", values)
|
||||
|
||||
class Genre(name: String, val id: String) : Filter.CheckBox(name)
|
||||
class GenreFilter(values: List<Genre>) : Filter.Group<Genre>("Géneros", values)
|
||||
|
||||
class Theme(name: String, val id: String) : Filter.CheckBox(name)
|
||||
class ThemeFilter(values: List<Theme>) : Filter.Group<Theme>("Temas", values)
|
||||
|
||||
open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
class OrderFilter() : UriPartFilter(
|
||||
"Ordenar por",
|
||||
arrayOf(
|
||||
Pair("Alfabético", "name"),
|
||||
Pair("Vistas", "views"),
|
||||
Pair("Fecha Estreno", "date"),
|
||||
),
|
||||
)
|
||||
|
||||
fun getFilterTypeList() = listOf(
|
||||
Type("Manga", "1"),
|
||||
Type("Manhwa", "2"),
|
||||
Type("Manhua", "3"),
|
||||
)
|
||||
|
||||
fun getFilterStatusList() = listOf(
|
||||
Status("En Marcha", "1"),
|
||||
Status("Terminado", "2"),
|
||||
Status("Detenido", "3"),
|
||||
Status("Pausado", "4"),
|
||||
)
|
||||
|
||||
fun getFilterDemographicList() = listOf(
|
||||
Demographic("Shonen", "1"),
|
||||
Demographic("Seinen", "2"),
|
||||
Demographic("Shojo", "3"),
|
||||
Demographic("Josei", "4"),
|
||||
)
|
||||
|
||||
fun getFilterContentList() = listOf(
|
||||
Content("Ecchi", "1"),
|
||||
Content("Gore", "2"),
|
||||
Content("Smut", "3"),
|
||||
Content("Violencia Sexual", "4"),
|
||||
)
|
||||
|
||||
fun getFilterFormatList() = listOf(
|
||||
Format("Adaptación", "14"),
|
||||
Format("Antalogía", "9"),
|
||||
Format("Color Completo", "18"),
|
||||
Format("Coloreado Oficial", "19"),
|
||||
Format("Coloreado Por Fan", "15"),
|
||||
Format("Creado Por Usuario", "20"),
|
||||
Format("Delincuencia", "16"),
|
||||
Format("Doujinshi", "10"),
|
||||
Format("Galardonado", "13"),
|
||||
Format("One Shot", "11"),
|
||||
Format("Tira Larga", "17"),
|
||||
Format("Webcomic", "12"),
|
||||
Format("YonKoma", "8"),
|
||||
)
|
||||
|
||||
fun getFilterGenreList() = listOf(
|
||||
Genre("Acción", "49"),
|
||||
Genre("Aventura", "50"),
|
||||
Genre("Boys Love", "75"),
|
||||
Genre("Chicas Mágicas", "73"),
|
||||
Genre("Ciencia-Ficción", "64"),
|
||||
Genre("Comedia", "51"),
|
||||
Genre("Crimen", "52"),
|
||||
Genre("Deporte", "65"),
|
||||
Genre("Drama", "53"),
|
||||
Genre("Fantasía", "54"),
|
||||
Genre("Filosófico", "61"),
|
||||
Genre("Girls Love", "76"),
|
||||
Genre("Guerra", "74"),
|
||||
Genre("Histórico", "55"),
|
||||
Genre("Horror", "56"),
|
||||
Genre("Isekai", "57"),
|
||||
Genre("Mecha", "58"),
|
||||
Genre("Médica", "59"),
|
||||
Genre("Misterio", "60"),
|
||||
Genre("Psicológico", "62"),
|
||||
Genre("Recuentos De La Vida", "72"),
|
||||
Genre("Romance", "63"),
|
||||
Genre("Superhéroe", "66"),
|
||||
Genre("Thriller", "67"),
|
||||
Genre("Tragedia", "68"),
|
||||
Genre("Wuxia", "69"),
|
||||
Genre("Yaoi", "70"),
|
||||
Genre("Yuri", "71"),
|
||||
)
|
||||
|
||||
fun getFilterThemeList() = listOf(
|
||||
Theme("Animales", "52"),
|
||||
Theme("Apocalíptico", "50"),
|
||||
Theme("Artes Marciales", "60"),
|
||||
Theme("Chicas Monstruo", "77"),
|
||||
Theme("Cocinando", "53"),
|
||||
Theme("Crossdressing", "79"),
|
||||
Theme("Delincuencia", "78"),
|
||||
Theme("Demonios", "54"),
|
||||
Theme("Extranjeros", "51"),
|
||||
Theme("Fantasma", "55"),
|
||||
Theme("Género Bender", "81"),
|
||||
Theme("Gyaru", "56"),
|
||||
Theme("Harén", "57"),
|
||||
Theme("Incesto", "58"),
|
||||
Theme("Lolicon", "59"),
|
||||
Theme("Mafia", "64"),
|
||||
Theme("Magia", "65"),
|
||||
Theme("Militar", "61"),
|
||||
Theme("Monstruos", "62"),
|
||||
Theme("Música", "63"),
|
||||
Theme("Ninja", "66"),
|
||||
Theme("Policía", "67"),
|
||||
Theme("Realidad Virtual", "74"),
|
||||
Theme("Reencarnación", "68"),
|
||||
Theme("Samurái", "73"),
|
||||
Theme("Shotacon", "71"),
|
||||
Theme("Sobrenatural", "69"),
|
||||
Theme("Superpoderes", "82"),
|
||||
Theme("Supervivencia", "72"),
|
||||
Theme("Vampiros", "75"),
|
||||
Theme("Vida Escolar", "70"),
|
||||
Theme("Videojuegos", "80"),
|
||||
Theme("Zombis", "76"),
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user