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 { ext {
extName = 'MonosChinos' extName = 'MonosChinos'
extClass = '.MonosChinos' extClass = '.MonosChinos'
extVersionCode = 29 extVersionCode = 30
} }
apply from: "$rootDir/common.gradle" 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.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import eu.kanade.tachiyomi.util.parseAs
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
@ -37,7 +36,7 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "MonosChinos" override val name = "MonosChinos"
override val baseUrl = "https://monoschinos2.com" override val baseUrl = "https://monoschinos2.net"
override val id = 6957694006954649296 override val id = 6957694006954649296
@ -80,7 +79,7 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
status = document.select(".lh-sm .ms-2").eachText().let { items -> status = document.select(".lh-sm .ms-2").eachText().let { items ->
when { when {
items.any { it.contains("Finalizado") } -> SAnime.COMPLETED 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 else -> SAnime.UNKNOWN
} }
} }
@ -88,12 +87,12 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
return animeDetails 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 { override fun popularAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup() val document = response.asJsoup()
val elements = document.select(".ficha_efecto a") 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 -> val animeList = elements.map { element ->
SAnime.create().apply { SAnime.create().apply {
title = element.selectFirst(".title_cap")!!.text() title = element.selectFirst(".title_cap")!!.text()
@ -106,13 +105,13 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
override fun latestUpdatesParse(response: Response) = popularAnimeParse(response) 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 { override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = MonosChinosFilters.getSearchParameters(filters) val params = MonosChinosFilters.getSearchParameters(filters)
return when { return when {
query.isNotBlank() -> GET("$baseUrl/buscar?q=$query", headers) query.isNotBlank() -> GET("$baseUrl/animes?buscar=$query&pag=$page", headers)
params.filter.isNotBlank() -> GET("$baseUrl/animes${params.getQuery()}&p=$page", headers) params.filter.isNotBlank() -> GET("$baseUrl/animes${params.getQuery()}&pag=$page", headers)
else -> popularAnimeRequest(page) else -> popularAnimeRequest(page)
} }
} }
@ -121,31 +120,50 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
override fun episodeListParse(response: Response): List<SEpisode> { override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup() 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 referer = document.location()
val dt = document.select("#dt")
val detail = getEpisodeDetails(capListLink, token, referer) val total = dt.attr("data-e").toInt()
val total = detail.eps.size val perPage = 50.0
val perPage = detail.perpage ?: return emptyList()
val pages = (total / perPage).ceilPage() val pages = (total / perPage).ceilPage()
val i = dt.attr("data-i")
val u = dt.attr("data-u")
var pageIdx = 1
return (1..pages).parallelCatchingFlatMapBlocking { return (1..pages).parallelCatchingFlatMapBlocking {
getEpisodePage(detail.paginateUrl ?: "", it, token, referer).caps.mapIndexed { idx, ep -> val formBody = FormBody.Builder()
val episodeNumber = (ep.episodio ?: (idx + 1)) .add("acc", "episodes")
SEpisode.create().apply { .add("i", i)
name = "Capítulo $episodeNumber" .add("u", u)
episode_number = episodeNumber.toFloat() .add("p", pageIdx.toString())
setUrlWithoutDomain(ep.url ?: "") .build()
}
} val request = Request.Builder()
}.reversed() .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 { override fun videoListParse(response: Response): List<Video> {
val formBody = FormBody.Builder().add("_token", token).build() 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() val request = Request.Builder()
.url(capListLink) .url("$baseUrl/ajax_pagination")
.post(formBody) .post(formBody)
.header("accept", "application/json, text/javascript, */*; q=0.01") .header("accept", "application/json, text/javascript, */*; q=0.01")
.header("accept-language", "es-419,es;q=0.8") .header("accept-language", "es-419,es;q=0.8")
@ -155,31 +173,9 @@ class MonosChinos : ConfigurableAnimeSource, AnimeHttpSource() {
.header("x-requested-with", "XMLHttpRequest") .header("x-requested-with", "XMLHttpRequest")
.build() .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 { return serverDocument.select("[data-player]")
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]")
.map { String(Base64.decode(it.attr("data-player"), Base64.DEFAULT)) } .map { String(Base64.decode(it.attr("data-player"), Base64.DEFAULT)) }
.parallelCatchingFlatMapBlocking { serverVideoResolver(it) } .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 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) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply { ListPreference(screen.context).apply {
key = PREF_SERVER_KEY key = PREF_SERVER_KEY

View file

@ -54,86 +54,118 @@ object MonosChinosFilters {
if (filters.isEmpty()) return FilterSearchParams() if (filters.isEmpty()) return FilterSearchParams()
return FilterSearchParams( return FilterSearchParams(
filters.parseCheckbox<GenresFilter>(DoramasytFiltersData.GENRES, "genero") + filters.parseCheckbox<GenresFilter>(DoramasytFiltersData.GENRES, "genero") +
filters.parseCheckbox<YearsFilter>(DoramasytFiltersData.YEARS, "anio") +
filters.parseCheckbox<TypesFilter>(DoramasytFiltersData.TYPES, "tipo") + filters.parseCheckbox<TypesFilter>(DoramasytFiltersData.TYPES, "tipo") +
filters.asQueryPart<YearsFilter>("fecha") + filters.asQueryPart<StatusFilter>("estado") +
filters.asQueryPart<LettersFilter>("letra"), filters.asQueryPart<SortFilter>("orden"),
) )
} }
val FILTER_LIST get() = AnimeFilterList( val FILTER_LIST get() = AnimeFilterList(
AnimeFilter.Header("La busqueda por texto ignora el filtro"), AnimeFilter.Header("La busqueda por texto ignora el filtro"),
GenresFilter(), GenresFilter(),
TypesFilter(),
YearsFilter(), YearsFilter(),
LettersFilter(), TypesFilter(),
StatusFilter(),
SortFilter(),
) )
class GenresFilter : CheckBoxFilterList("Género", DoramasytFiltersData.GENRES.map { CheckBoxVal(it.first, false) }) 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 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 { private object DoramasytFiltersData {
val TYPES = arrayOf( val TYPES = arrayOf(
Pair("<Selecionar>", ""),
Pair("Pelicula", "pelicula"),
Pair("Anime", "anime"), 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( val GENRES = arrayOf(
Pair("<Selecionar>", ""),
Pair("Acción", "accion"), Pair("Acción", "accion"),
Pair("Aenime", "aenime"),
Pair("Anime Latino", "anime-latino"),
Pair("Artes Marciales", "artes-marciales"),
Pair("Aventura", "aventura"), Pair("Aventura", "aventura"),
Pair("Aventuras", "aventuras"),
Pair("Blu-ray", "blu-ray"),
Pair("Carreras", "carreras"), Pair("Carreras", "carreras"),
Pair("Castellano", "castellano"),
Pair("Ciencia Ficción", "ciencia-ficcion"), Pair("Ciencia Ficción", "ciencia-ficcion"),
Pair("Comedia", "comedia"), Pair("Comedia", "comedia"),
Pair("Cyberpunk", "cyberpunk"), Pair("Cyberpunk", "cyberpunk"),
Pair("Demencia", "demencia"),
Pair("Dementia", "dementia"),
Pair("Demonios", "demonios"),
Pair("Deportes", "deportes"), Pair("Deportes", "deportes"),
Pair("Drama", "drama"), Pair("Drama", "drama"),
Pair("Ecchi", "ecchi"), Pair("Ecchi", "ecchi"),
Pair("Escolares", "escolares"), Pair("Escolares", "escolares"),
Pair("Espacial", "espacial"),
Pair("Fantasía", "fantasia"), Pair("Fantasía", "fantasia"),
Pair("Gore", "gore"), Pair("Gore", "gore"),
Pair("Harem", "harem"), Pair("Harem", "harem"),
Pair("Historia paralela", "historia-paralela"),
Pair("Historico", "historico"),
Pair("Horror", "horror"), Pair("Horror", "horror"),
Pair("Infantil", "infantil"),
Pair("Josei", "josei"), Pair("Josei", "josei"),
Pair("Juegos", "juegos"),
Pair("Latino", "latino"),
Pair("Lucha", "lucha"), Pair("Lucha", "lucha"),
Pair("Magia", "magia"), Pair("Magia", "magia"),
Pair("Mecha", "mecha"), Pair("Mecha", "mecha"),
Pair("Militar", "militar"), Pair("Militar", "militar"),
Pair("Misterio", "misterio"), Pair("Misterio", "misterio"),
Pair("Monogatari", "monogatari"),
Pair("Música", "musica"), Pair("Música", "musica"),
Pair("Parodia", "parodia"),
Pair("Parodias", "parodias"), Pair("Parodias", "parodias"),
Pair("Policía", "policia"),
Pair("Psicológico", "psicologico"), Pair("Psicológico", "psicologico"),
Pair("Recuentos de la vida", "recuentos-de-la-vida"),
Pair("Recuerdos de la vida", "recuerdos-de-la-vida"), Pair("Recuerdos de la vida", "recuerdos-de-la-vida"),
Pair("Romance", "romance"),
Pair("Samurai", "samurai"),
Pair("Seinen", "seinen"), Pair("Seinen", "seinen"),
Pair("Shojo", "shojo"), Pair("Shojo", "shojo"),
Pair("Shonen", "shonen"), Pair("Shonen", "shonen"),
Pair("Shoujo", "shoujo"),
Pair("Shounen", "shounen"),
Pair("Sobrenatural", "sobrenatural"), Pair("Sobrenatural", "sobrenatural"),
Pair("Superpoderes", "superpoderes"),
Pair("Suspenso", "suspenso"),
Pair("Terror", "terror"),
Pair("Vampiros", "vampiros"), Pair("Vampiros", "vampiros"),
Pair("Yaoi", "yaoi"), Pair("Yaoi", "yaoi"),
Pair("Yuri", "yuri"), Pair("Yuri", "yuri"),
Pair("Latino", "latino"), )
Pair("Espacial", "espacial"),
Pair("Histórico", "historico"), val SORT = arrayOf(
Pair("Samurai", "samurai"), Pair("Descendente", "desc"),
Pair("Artes Marciales", "artes-marciales"), Pair("Ascendente", "asc"),
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"),
) )
} }
} }

View file

@ -1,7 +1,7 @@
ext { ext {
extName = 'Pelisplushd' extName = 'Pelisplushd'
extClass = '.PelisplushdFactory' extClass = '.PelisplushdFactory'
extVersionCode = 59 extVersionCode = 60
} }
apply from: "$rootDir/common.gradle" 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() { open class Pelisplushd(override val name: String, override val baseUrl: String) : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val id: Long = 1400819034564144238L
override val lang = "es" override val lang = "es"
override val supportsLatest = false override val supportsLatest = false

View file

@ -7,6 +7,6 @@ class PelisplushdFactory : AnimeSourceFactory {
override fun createSources(): List<AnimeSource> = listOf( override fun createSources(): List<AnimeSource> = listOf(
Pelisplushd("PelisPlusHD", "https://pelisplushd.bz"), Pelisplushd("PelisPlusHD", "https://pelisplushd.bz"),
Pelisplusto("PelisPlusTo", "https://ww3.pelisplus.to"), 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) { class Pelisplusph(override val name: String, override val baseUrl: String) : Pelisplushd(name, baseUrl) {
override val id: Long = 4917265654298497443L
override val supportsLatest = false override val supportsLatest = false
companion object { 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() anime.title = document.selectFirst(".info-content h1")!!.text()
document.select(".info-content p").map { p -> document.select(".info-content p").map { p ->
if (p.select(".content-type").text().contains("Sinópsis:")) { 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:")) { if (p.select(".content-type").text().contains("Géneros:")) {
anime.genre = p.select(".content-type-a a").joinToString { it.text() } 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:")) { if (p.select(".content-type").text().contains("Reparto:")) {
anime.artist = p.select(".content-type ~ span").text().substringBefore(",") 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 = anime.status =
if (document.location().contains("/serie/")) SAnime.UNKNOWN else SAnime.COMPLETED 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) { class Pelisplusto(override val name: String, override val baseUrl: String) : Pelisplushd(name, baseUrl) {
override val id: Long = 1705636111422561130L
private val json: Json by injectLazy() private val json: Json by injectLazy()
override val supportsLatest = false override val supportsLatest = false