New Source: Cineplus123 (es) (#107)

This commit is contained in:
Dark25 2024-08-05 15:07:35 +01:00 committed by GitHub
parent dcb5c7ef2f
commit 802f56295e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 391 additions and 0 deletions

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".es.cineplus123.Cineplus123UrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="cineplus123.org"
android:pathPattern="/anime/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,14 @@
ext {
extName = 'Cineplus123'
extClass = '.Cineplus123'
themePkg = 'dooplay'
baseUrl = 'https://cineplus123.org'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:streamwish-extractor"))
implementation(project(":lib:uqload-extractor"))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View file

@ -0,0 +1,196 @@
package eu.kanade.tachiyomi.animeextension.es.cineplus123
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelFlatMapBlocking
import okhttp3.FormBody
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Element
class Cineplus123 : DooPlay(
"es",
"Cineplus123",
"https://cineplus123.org",
) {
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/tendencias/$page")
override fun popularAnimeSelector() = latestUpdatesSelector()
override fun popularAnimeNextPageSelector() = latestUpdatesNextPageSelector()
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/ano/2024/page/$page", headers)
override fun videoListSelector() = "li.dooplay_player_option" // ul#playeroptionsul
override val episodeMovieText = "Película"
override val episodeSeasonPrefix = "Temporada"
override val prefQualityTitle = "Calidad preferida"
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val players = document.select("ul#playeroptionsul li")
return players.parallelFlatMapBlocking { player ->
val name = player.selectFirst("span.title")!!.text()
val url = getPlayerUrl(player)
?: return@parallelFlatMapBlocking emptyList<Video>()
extractVideos(url, name)
}
}
private fun extractVideos(url: String, lang: String): List<Video> {
return when {
"uqload" in url -> uqloadExtractor.videosFromUrl(url, "$lang -")
"strwish" in url -> streamWishExtractor.videosFromUrl(url, lang)
else -> null
} ?: emptyList()
}
private fun getPlayerUrl(player: Element): String? {
val body = FormBody.Builder()
.add("action", "doo_player_ajax")
.add("post", player.attr("data-post"))
.add("nume", player.attr("data-nume"))
.add("type", player.attr("data-type"))
.build()
return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", headers, body))
.execute().body.string()
.substringAfter("\"embed_url\":\"")
.substringBefore("\",")
.replace("\\", "")
.takeIf(String::isNotBlank)
}
// ============================== Filters ===============================
override val fetchGenres = false
override fun getFilterList() = Cineplus123Filters.FILTER_LIST
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = Cineplus123Filters.getSearchParameters(filters)
val path = when {
params.genre.isNotBlank() -> {
if (params.genre in listOf("tendencias", "ratings", "series-de-tv", "peliculas")) {
"/${params.genre}"
} else {
"/genero/${params.genre}"
}
}
params.language.isNotBlank() -> "/genero/${params.language}"
params.year.isNotBlank() -> "/ano/${params.year}"
params.movie.isNotBlank() -> {
if (params.movie == "Peliculas") {
"/peliculas"
} else {
"/genero/${params.movie}"
}
}
else -> buildString {
append(
when {
query.isNotBlank() -> "/?s=$query"
else -> "/"
},
)
append(
when (params.type) {
"serie" -> "serie-de-tv"
"pelicula" -> "peliculas"
else -> "tendencias"
},
)
if (params.isInverted) append("&orden=asc")
}
}
return if (path.startsWith("/?s=")) {
GET("$baseUrl/page/$page$path")
} else {
GET("$baseUrl$path/page/$page")
}
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
super.setupPreferenceScreen(screen) // Quality preference
val langPref = ListPreference(screen.context).apply {
key = PREF_LANG_KEY
title = PREF_LANG_TITLE
entries = PREF_LANG_ENTRIES
entryValues = PREF_LANG_VALUES
setDefaultValue(PREF_LANG_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
ListPreference(screen.context).apply {
key = PREF_SERVER_KEY
title = "Preferred server"
entries = SERVER_LIST
entryValues = SERVER_LIST
setDefaultValue(PREF_SERVER_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}.also(screen::addPreference)
screen.addPreference(langPref)
}
// ============================= Utilities ==============================
override fun String.toDate() = 0L
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(prefQualityKey, prefQualityDefault)!!
val lang = preferences.getString(PREF_LANG_KEY, PREF_LANG_DEFAULT)!!
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
return sortedWith(
compareBy(
{ it.quality.contains(lang) },
{ it.quality.contains(server, true) },
{ it.quality.contains(quality) },
),
).reversed()
}
override val prefQualityValues = arrayOf("480p", "720p", "1080p")
override val prefQualityEntries = prefQualityValues
companion object {
private const val PREF_LANG_KEY = "preferred_lang"
private const val PREF_LANG_TITLE = "Preferred language"
private const val PREF_LANG_DEFAULT = "LATINO"
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "Uqload"
private val PREF_LANG_ENTRIES = arrayOf("SUBTITULADO", "LATINO", "CASTELLANO")
private val PREF_LANG_VALUES = arrayOf("SUBTITULADO", "LATINO", "CASTELLANO")
private val SERVER_LIST = arrayOf("StreamWish", "Uqload")
}
}

View file

@ -0,0 +1,159 @@
package eu.kanade.tachiyomi.animeextension.es.cineplus123
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
object Cineplus123Filters {
open class UriPartFilter(
displayName: String,
private val vals: Array<Pair<String, String>>,
) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray(),
) {
fun toUriPart() = vals[state].second
}
private inline fun <reified R> AnimeFilterList.getFirst(): R {
return first { it is R } as R
}
private inline fun <reified R> AnimeFilterList.asUriPart(): String {
return getFirst<R>().let {
(it as UriPartFilter).toUriPart()
}
}
class InvertedResultsFilter : AnimeFilter.CheckBox("Invertir resultados", false)
class TypeFilter : UriPartFilter("Tipo", AnimesOnlineNinjaData.TYPES)
class GenreFilter : UriPartFilter("Generos", AnimesOnlineNinjaData.GENRES)
class LanguageFilter : UriPartFilter("Idiomas", AnimesOnlineNinjaData.LANGUAGES)
class YearFilter : UriPartFilter("Año", AnimesOnlineNinjaData.YEARS)
class MovieFilter : UriPartFilter("Peliculas", AnimesOnlineNinjaData.MOVIES)
class OtherOptionsGroup : AnimeFilter.Group<UriPartFilter>(
"Otros filtros",
listOf(
GenreFilter(),
LanguageFilter(),
YearFilter(),
MovieFilter(),
),
)
private inline fun <reified R> AnimeFilter.Group<UriPartFilter>.getItemUri(): String {
return state.first { it is R }.toUriPart()
}
val FILTER_LIST get() = AnimeFilterList(
InvertedResultsFilter(),
TypeFilter(),
AnimeFilter.Separator(),
AnimeFilter.Header("Estos filtros no afectan a la busqueda por texto"),
OtherOptionsGroup(),
)
data class FilterSearchParams(
val isInverted: Boolean = false,
val type: String = "",
val genre: String = "",
val language: String = "",
val year: String = "",
val movie: String = "",
)
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
if (filters.isEmpty()) return FilterSearchParams()
val others = filters.getFirst<OtherOptionsGroup>()
return FilterSearchParams(
filters.getFirst<InvertedResultsFilter>().state,
filters.asUriPart<TypeFilter>(),
others.getItemUri<GenreFilter>(),
others.getItemUri<LanguageFilter>(),
others.getItemUri<YearFilter>(),
others.getItemUri<MovieFilter>(),
)
}
private object AnimesOnlineNinjaData {
val EVERY = Pair("Seleccionar", "")
val TYPES = arrayOf(
Pair("Todos", "todos"),
Pair("Series", "serie"),
Pair("Peliculas", "pelicula"),
)
val GENRES = arrayOf(
EVERY,
Pair("accion", "accion"),
Pair("action-adventure", "action-adventure"),
Pair("animacion", "animacion"),
Pair("aventura", "aventura"),
Pair("bajalogratis", "bajalogratis"),
Pair("belica", "belica"),
Pair("ciencia-ficcion", "ciencia-ficcion"),
Pair("comedia", "comedia"),
Pair("crimen", "crimen"),
Pair("disney", "disney"),
Pair("documental", "documental"),
Pair("don-torrent", "don-torrent"),
Pair("drama", "drama"),
Pair("familia", "familia"),
Pair("fantasia", "fantasia"),
Pair("gran-torrent", "gran-torrent"),
Pair("hbo", "hbo"),
Pair("historia", "historia"),
Pair("kids", "kids"),
Pair("misterio", "misterio"),
Pair("musica", "musica"),
Pair("romance", "romance"),
Pair("sci-fi-fantasy", "sci-fi-fantasy"),
Pair("series-de-amazon-prime-video", "series-de-amazon-prime-video"),
Pair("soap", "soap"),
Pair("suspense", "suspense"),
Pair("talk", "talk"),
Pair("terror", "terror"),
Pair("war-politics", "war-politics"),
Pair("western", "western"),
)
val LANGUAGES = arrayOf(
EVERY,
Pair("latino", "latino"),
Pair("castellano", "castellano"),
Pair("subtitulado", "subtitulado"),
)
val YEARS = arrayOf(EVERY) + (2024 downTo 1979).map {
Pair(it.toString(), it.toString())
}.toTypedArray()
val MOVIES = arrayOf(
EVERY,
Pair("pelicula", "pelicula"),
Pair("series", "series de tv"),
Pair("pelicula-de-tv", "pelicula-de-tv"),
Pair("peliculas-cristianas", "peliculas-cristianas"),
Pair("peliculas-de-halloween", "peliculas-de-halloween"),
Pair("peliculas-de-navidad", "peliculas-de-navidad"),
Pair("peliculas-para-el-dia-de-la-madre", "peliculas-para-el-dia-de-la-madre"),
Pair("pelis-play", "pelis-play"),
Pair("pelishouse", "pelishouse"),
Pair("pelismart-tv", "pelismart-tv"),
Pair("pelisnow", "pelisnow"),
Pair("pelix-tv", "pelix-tv"),
Pair("poseidonhd", "poseidonhd"),
Pair("proximamente", "proximamente"),
Pair("reality", "reality"),
Pair("repelis-go", "repelis-go"),
Pair("repelishd-tv", "repelishd-tv"),
Pair("repelisplus", "repelisplus"),
)
}
}