fix(src/es): PelisPlusPh & MonosChinos fixes #497

Merged
imper1aldev merged 2 commits from fixes into main 2025-01-08 14:50:22 -06:00
8 changed files with 130 additions and 78 deletions

View file

@ -1,7 +1,7 @@
ext {
extName = 'MonosChinos'
extClass = '.MonosChinos'
extVersionCode = 29
extVersionCode = 30
}
apply from: "$rootDir/common.gradle"

View file

@ -24,7 +24,6 @@ import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import eu.kanade.tachiyomi.util.parseAs
import okhttp3.FormBody
import okhttp3.Request
import okhttp3.Response
@ -37,7 +36,7 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "MonosChinos"
override val baseUrl = "https://monoschinos2.com"
override val baseUrl = "https://monoschinos2.net"
override val id = 6957694006954649296
@ -80,7 +79,7 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
status = document.select(".lh-sm .ms-2").eachText().let { items ->
when {
items.any { it.contains("Finalizado") } -> SAnime.COMPLETED
items.any { it.contains("Estreno") } -> SAnime.ONGOING
items.any { it.contains("En emision") || it.contains("Estreno") } -> SAnime.ONGOING
else -> SAnime.UNKNOWN
}
}
@ -88,12 +87,12 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
return animeDetails
}
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/animes?p=$page", headers)
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/animes?pag=$page", headers)
override fun popularAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val elements = document.select(".ficha_efecto a")
val nextPage = document.select(".pagination [rel=\"next\"]").any()
val nextPage = document.select(".pagination [title=\"Siguiente página\"]").any()
val animeList = elements.map { element ->
SAnime.create().apply {
title = element.selectFirst(".title_cap")!!.text()
@ -106,13 +105,13 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/emision?p=$page", headers)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/animes?estado=en+emision&pag=$page", headers)
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = MonosChinosFilters.getSearchParameters(filters)
return when {
query.isNotBlank() -> GET("$baseUrl/buscar?q=$query", headers)
params.filter.isNotBlank() -> GET("$baseUrl/animes${params.getQuery()}&p=$page", headers)
query.isNotBlank() -> GET("$baseUrl/animes?buscar=$query&pag=$page", headers)
params.filter.isNotBlank() -> GET("$baseUrl/animes${params.getQuery()}&pag=$page", headers)
else -> popularAnimeRequest(page)
}
}
@ -121,31 +120,50 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val token = document.select("meta[name='csrf-token']").attr("content")
val capListLink = document.select(".caplist").attr("data-ajax")
val referer = document.location()
val dt = document.select("#dt")
val detail = getEpisodeDetails(capListLink, token, referer)
val total = detail.eps.size
val perPage = detail.perpage ?: return emptyList()
val total = dt.attr("data-e").toInt()
val perPage = 50.0
val pages = (total / perPage).ceilPage()
val i = dt.attr("data-i")
val u = dt.attr("data-u")
var pageIdx = 1
return (1..pages).parallelCatchingFlatMapBlocking {
getEpisodePage(detail.paginateUrl ?: "", it, token, referer).caps.mapIndexed { idx, ep ->
val episodeNumber = (ep.episodio ?: (idx + 1))
SEpisode.create().apply {
name = "Capítulo $episodeNumber"
episode_number = episodeNumber.toFloat()
setUrlWithoutDomain(ep.url ?: "")
}
}
}.reversed()
val formBody = FormBody.Builder()
.add("acc", "episodes")
.add("i", i)
.add("u", u)
.add("p", pageIdx.toString())
.build()
val request = Request.Builder()
.url("$baseUrl/ajax_pagination")
.post(formBody)
.header("accept", "application/json, text/javascript, */*; q=0.01")
.header("accept-language", "es-419,es;q=0.8")
.header("content-type", "application/x-www-form-urlencoded; charset=UTF-8")
.header("origin", baseUrl)
.header("referer", referer)
.header("x-requested-with", "XMLHttpRequest")
.build()
pageIdx++
client.newCall(request).execute().getEpisodes()
}
}
private fun getEpisodeDetails(capListLink: String, token: String, referer: String): EpisodesDto {
val formBody = FormBody.Builder().add("_token", token).build()
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val i = document.select(".opt").attr("data-encrypt")
val referer = document.location()
val formBody = FormBody.Builder()
.add("acc", "opt")
.add("i", i)
.build()
val request = Request.Builder()
.url(capListLink)
.url("$baseUrl/ajax_pagination")
.post(formBody)
.header("accept", "application/json, text/javascript, */*; q=0.01")
.header("accept-language", "es-419,es;q=0.8")
@ -155,31 +173,9 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
.header("x-requested-with", "XMLHttpRequest")
.build()
return client.newCall(request).execute().parseAs<EpisodesDto>()
}
val serverDocument = client.newCall(request).execute().asJsoup()
private fun getEpisodePage(paginateUrl: String, page: Int, token: String, referer: String): EpisodeInfoDto {
val formBodyEp = FormBody.Builder()
.add("_token", token)
.add("p", "$page")
.build()
val requestEp = Request.Builder()
.url(paginateUrl)
.post(formBodyEp)
.header("accept", "application/json, text/javascript, */*; q=0.01")
.header("accept-language", "es-419,es;q=0.8")
.header("content-type", "application/x-www-form-urlencoded; charset=UTF-8")
.header("origin", baseUrl)
.header("referer", referer)
.header("x-requested-with", "XMLHttpRequest")
.build()
return client.newCall(requestEp).execute().parseAs<EpisodeInfoDto>()
}
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
return document.select("[data-player]")
return serverDocument.select("[data-player]")
.map { String(Base64.decode(it.attr("data-player"), Base64.DEFAULT)) }
.parallelCatchingFlatMapBlocking { serverVideoResolver(it) }
}
@ -242,6 +238,21 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
private fun Double.ceilPage(): Int = if (this % 1 == 0.0) this.toInt() else ceil(this).toInt()
private fun Response.getEpisodes(): List<SEpisode> {
val document = this.asJsoup()
return document.select(".ko").mapIndexed { idx, it ->
val episodeNumber = try {
it.select("h2").text().substringAfter("Capítulo").trim().toFloat()
} catch (e: Exception) { idx + 1f }
SEpisode.create().apply {
name = it.select(".fs-6").text()
episode_number = episodeNumber
setUrlWithoutDomain(it.attr("abs:href"))
}
}
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_SERVER_KEY

View file

@ -54,86 +54,118 @@ object MonosChinosFilters {
if (filters.isEmpty()) return FilterSearchParams()
return FilterSearchParams(
filters.parseCheckbox<GenresFilter>(DoramasytFiltersData.GENRES, "genero") +
filters.parseCheckbox<YearsFilter>(DoramasytFiltersData.YEARS, "anio") +
filters.parseCheckbox<TypesFilter>(DoramasytFiltersData.TYPES, "tipo") +
filters.asQueryPart<YearsFilter>("fecha") +
filters.asQueryPart<LettersFilter>("letra"),
filters.asQueryPart<StatusFilter>("estado") +
filters.asQueryPart<SortFilter>("orden"),
)
}
val FILTER_LIST get() = AnimeFilterList(
AnimeFilter.Header("La busqueda por texto ignora el filtro"),
GenresFilter(),
TypesFilter(),
YearsFilter(),
LettersFilter(),
TypesFilter(),
StatusFilter(),
SortFilter(),
)
class GenresFilter : CheckBoxFilterList("Género", DoramasytFiltersData.GENRES.map { CheckBoxVal(it.first, false) })
class YearsFilter : CheckBoxFilterList("Año", DoramasytFiltersData.YEARS.map { CheckBoxVal(it.first, false) })
class TypesFilter : CheckBoxFilterList("Tipo", DoramasytFiltersData.TYPES.map { CheckBoxVal(it.first, false) })
class YearsFilter : QueryPartFilter("Año", DoramasytFiltersData.YEARS)
class StatusFilter : QueryPartFilter("Estado", DoramasytFiltersData.STATUS)
class LettersFilter : QueryPartFilter("Letra", DoramasytFiltersData.LETTER)
class SortFilter : QueryPartFilter("Orden", DoramasytFiltersData.SORT)
private object DoramasytFiltersData {
val TYPES = arrayOf(
Pair("<Selecionar>", ""),
Pair("Pelicula", "pelicula"),
Pair("Anime", "anime"),
Pair("Audio Japonés", "audio-japones"),
Pair("Corto", "corto"),
Pair("Donghua", "donghua"),
Pair("Especial", "especial"),
Pair("Ona", "ona"),
Pair("Ova", "ova"),
Pair("Película", "pelicula"),
Pair("Película 1080p", "pelicula-1080p"),
Pair("TV", "tv"),
Pair("Sin Censura", "sin-censura"),
)
val YEARS = arrayOf(Pair("<Seleccionar>", "")) + (1982..Calendar.getInstance().get(Calendar.YEAR)).map { Pair("$it", "$it") }.reversed().toTypedArray()
val STATUS = arrayOf(
Pair("Todos", ""),
Pair("En emisión", "en-emision"),
Pair("Finalizado", "finalizado"),
)
val LETTER = arrayOf(Pair("<Seleccionar>", "")) + ('A'..'Z').map { Pair("$it", "$it") }.toTypedArray()
val YEARS = (1968..Calendar.getInstance().get(Calendar.YEAR)).map { Pair("$it", "$it") }.reversed().toTypedArray()
val GENRES = arrayOf(
Pair("<Selecionar>", ""),
Pair("Acción", "accion"),
Pair("Aenime", "aenime"),
Pair("Anime Latino", "anime-latino"),
Pair("Artes Marciales", "artes-marciales"),
Pair("Aventura", "aventura"),
Pair("Aventuras", "aventuras"),
Pair("Blu-ray", "blu-ray"),
Pair("Carreras", "carreras"),
Pair("Castellano", "castellano"),
Pair("Ciencia Ficción", "ciencia-ficcion"),
Pair("Comedia", "comedia"),
Pair("Cyberpunk", "cyberpunk"),
Pair("Demencia", "demencia"),
Pair("Dementia", "dementia"),
Pair("Demonios", "demonios"),
Pair("Deportes", "deportes"),
Pair("Drama", "drama"),
Pair("Ecchi", "ecchi"),
Pair("Escolares", "escolares"),
Pair("Espacial", "espacial"),
Pair("Fantasía", "fantasia"),
Pair("Gore", "gore"),
Pair("Harem", "harem"),
Pair("Historia paralela", "historia-paralela"),
Pair("Historico", "historico"),
Pair("Horror", "horror"),
Pair("Infantil", "infantil"),
Pair("Josei", "josei"),
Pair("Juegos", "juegos"),
Pair("Latino", "latino"),
Pair("Lucha", "lucha"),
Pair("Magia", "magia"),
Pair("Mecha", "mecha"),
Pair("Militar", "militar"),
Pair("Misterio", "misterio"),
Pair("Monogatari", "monogatari"),
Pair("Música", "musica"),
Pair("Parodia", "parodia"),
Pair("Parodias", "parodias"),
Pair("Policía", "policia"),
Pair("Psicológico", "psicologico"),
Pair("Recuentos de la vida", "recuentos-de-la-vida"),
Pair("Recuerdos de la vida", "recuerdos-de-la-vida"),
Pair("Romance", "romance"),
Pair("Samurai", "samurai"),
Pair("Seinen", "seinen"),
Pair("Shojo", "shojo"),
Pair("Shonen", "shonen"),
Pair("Shoujo", "shoujo"),
Pair("Shounen", "shounen"),
Pair("Sobrenatural", "sobrenatural"),
Pair("Superpoderes", "superpoderes"),
Pair("Suspenso", "suspenso"),
Pair("Terror", "terror"),
Pair("Vampiros", "vampiros"),
Pair("Yaoi", "yaoi"),
Pair("Yuri", "yuri"),
Pair("Latino", "latino"),
Pair("Espacial", "espacial"),
Pair("Histórico", "historico"),
Pair("Samurai", "samurai"),
Pair("Artes Marciales", "artes-marciales"),
Pair("Demonios", "demonios"),
Pair("Romance", "romance"),
Pair("Dementia", "dementia"),
Pair(" Policía", "policia"),
Pair("Castellano", "castellano"),
Pair("Historia paralela", "historia-paralela"),
Pair("Aenime", "aenime"),
Pair("Blu-ray", "blu-ray"),
Pair("Monogatari", "monogatari"),
)
val SORT = arrayOf(
Pair("Descendente", "desc"),
Pair("Ascendente", "asc"),
)
}
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'Pelisplushd'
extClass = '.PelisplushdFactory'
extVersionCode = 59
extVersionCode = 60
}
apply from: "$rootDir/common.gradle"

View file

@ -39,6 +39,8 @@ import uy.kohesive.injekt.api.get
open class Pelisplushd(override val name: String, override val baseUrl: String) : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val id: Long = 1400819034564144238L
override val lang = "es"
override val supportsLatest = false

View file

@ -7,6 +7,6 @@ class PelisplushdFactory : AnimeSourceFactory {
override fun createSources(): List<AnimeSource> = listOf(
Pelisplushd("PelisPlusHD", "https://pelisplushd.bz"),
Pelisplusto("PelisPlusTo", "https://ww3.pelisplus.to"),
Pelisplusph("PelisPlusPh", "https://www.pelisplushd.ph"),
Pelisplusph("PelisPlusPh", "https://ww5.pelisplushd.pe"),
)
}

View file

@ -16,6 +16,8 @@ import org.jsoup.nodes.Element
class Pelisplusph(override val name: String, override val baseUrl: String) : Pelisplushd(name, baseUrl) {
override val id: Long = 4917265654298497443L
override val supportsLatest = false
companion object {
@ -52,7 +54,7 @@ class Pelisplusph(override val name: String, override val baseUrl: String) : Pel
anime.title = document.selectFirst(".info-content h1")!!.text()
document.select(".info-content p").map { p ->
if (p.select(".content-type").text().contains("Sinópsis:")) {
anime.description = p.select(".sinopsis")!!.text()
anime.description = p.select(".sinopsis").text()
}
if (p.select(".content-type").text().contains("Géneros:")) {
anime.genre = p.select(".content-type-a a").joinToString { it.text() }
@ -60,6 +62,9 @@ class Pelisplusph(override val name: String, override val baseUrl: String) : Pel
if (p.select(".content-type").text().contains("Reparto:")) {
anime.artist = p.select(".content-type ~ span").text().substringBefore(",")
}
if (p.select(".content-type").text().contains("Actores:")) {
anime.artist = p.select(".content-type ~ span").text().substringBefore(",")
}
}
anime.status =
if (document.location().contains("/serie/")) SAnime.UNKNOWN else SAnime.COMPLETED

View file

@ -23,6 +23,8 @@ import uy.kohesive.injekt.injectLazy
class Pelisplusto(override val name: String, override val baseUrl: String) : Pelisplushd(name, baseUrl) {
override val id: Long = 1705636111422561130L
private val json: Json by injectLazy()
override val supportsLatest = false