Merged with dark25 (#636)

* merge

merged lib, lib-multisrc, all, ar, de, en, es, fr, hi, id, it, pt, tr src from dark25

* patch
This commit is contained in:
Hak 2025-02-10 15:41:59 +07:00 committed by GitHub
parent 9f385108fc
commit 1384df62f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
350 changed files with 12176 additions and 1064 deletions

View file

@ -0,0 +1,15 @@
ext {
extName = 'Animenix'
extClass = '.Animenix'
themePkg = 'dooplay'
baseUrl = 'https://animenix.com'
overrideVersionCode = 8
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:filemoon-extractor"))
implementation(project(":lib:streamwish-extractor"))
implementation(project(":lib:universal-extractor"))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,215 @@
package eu.kanade.tachiyomi.animeextension.es.animenix
import androidx.preference.CheckBoxPreference
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.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
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 okhttp3.FormBody
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
class Animenix : DooPlay(
"es",
"Animenix",
"https://animenix.com",
) {
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/ratings/$page")
override fun popularAnimeSelector() = latestUpdatesSelector()
override fun popularAnimeNextPageSelector() = latestUpdatesNextPageSelector()
// =============================== Search ===============================
// ============================== Episodes ==============================
override val episodeMovieText = "Película"
override fun videoListParse(response: Response): List<Video> {
val players = response.asJsoup().select("li.dooplay_player_option")
return players.flatMap { player ->
runCatching {
val link = getPlayerUrl(player)
getPlayerVideos(link)
}.getOrElse { 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()
.let { response ->
response.body.string()
.substringAfter("\"embed_url\":\"")
.substringBefore("\",")
.replace("\\", "")
}
}
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
private val streamWishExtractor by lazy { StreamWishExtractor(headers = headers, client = client) }
private val universalExtractor by lazy { UniversalExtractor(client) }
private fun getPlayerVideos(link: String): List<Video> {
return when {
link.contains("filemoon") -> filemoonExtractor.videosFromUrl(link)
link.contains("swdyu") -> streamWishExtractor.videosFromUrl(link)
link.contains("wishembed") || link.contains("cdnwish") || link.contains("flaswish") || link.contains("sfastwish") || link.contains("streamwish") || link.contains("asnwish") -> streamWishExtractor.videosFromUrl(link)
else -> universalExtractor.videosFromUrl(link, headers)
}
}
// =========================== Anime Details ============================
override fun Document.getDescription(): String {
return select("$additionalInfoSelector div.wp-content p")
.eachText()
.joinToString("\n")
}
override val additionalInfoItems = listOf("Título", "Temporadas", "Episodios", "Duración media")
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/ver/page/$page", headers)
override fun latestUpdatesNextPageSelector() = "div.pagination > *:last-child:not(span):not(.current)"
// =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = AnimenixFilters.getSearchParameters(filters)
val path = when {
params.genre.isNotBlank() -> {
if (params.genre in listOf("tendencias", "ratings")) {
"/" + params.genre
} else {
"/genero/${params.genre}"
}
}
params.language.isNotBlank() -> "/genero/${params.language}"
params.year.isNotBlank() -> "/release/${params.year}"
params.movie.isNotBlank() -> {
if (params.movie == "pelicula") {
"/pelicula"
} else {
"/genero/${params.movie}"
}
}
else -> buildString {
append(
when {
query.isNotBlank() -> "/?s=$query"
params.letter.isNotBlank() -> "/letra/${params.letter}/?"
else -> "/tendencias/?"
},
)
append(
if (contains("tendencias")) {
"&get=${when (params.type){
"anime" -> "serie"
"pelicula" -> "pelicula"
else -> "todos"
}}"
} else {
"&tipo=${params.type}"
},
)
if (params.isInverted) append("&orden=asc")
}
}
return if (path.startsWith("/?s=")) {
GET("$baseUrl/page/$page$path")
} else if (path.startsWith("/letra") || path.startsWith("/tendencias")) {
val before = path.substringBeforeLast("/")
val after = path.substringAfterLast("/")
GET("$baseUrl$before/page/$page/$after")
} else {
GET("$baseUrl$path/page/$page")
}
}
// ============================== Filters ===============================
override val fetchGenres = false
override fun getFilterList() = AnimenixFilters.FILTER_LIST
// ============================== Settings ==============================
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()
}
}
val vrfIterceptPref = CheckBoxPreference(screen.context).apply {
key = PREF_VRF_INTERCEPT_KEY
title = PREF_VRF_INTERCEPT_TITLE
summary = PREF_VRF_INTERCEPT_SUMMARY
setDefaultValue(PREF_VRF_INTERCEPT_DEFAULT)
}
screen.addPreference(vrfIterceptPref)
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)!!
return sortedWith(
compareBy(
{ it.quality.contains(lang) },
{ 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 = "SUB"
private val PREF_LANG_ENTRIES = arrayOf("SUB", "All", "ES", "LAT")
private val PREF_LANG_VALUES = arrayOf("SUB", "", "ES", "LAT")
private const val PREF_VRF_INTERCEPT_KEY = "vrf_intercept"
private const val PREF_VRF_INTERCEPT_TITLE = "Intercept VRF links (Requiere Reiniciar)"
private const val PREF_VRF_INTERCEPT_SUMMARY = "Intercept VRF links and open them in the browser"
private const val PREF_VRF_INTERCEPT_DEFAULT = false
}
}

View file

@ -0,0 +1,152 @@
package eu.kanade.tachiyomi.animeextension.es.animenix
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
object AnimenixFilters {
open class UriPartFilter(
displayName: String,
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 LetterFilter : UriPartFilter("Filtrar por letra", AnimesOnlineNinjaData.LETTERS)
class GenreFilter : UriPartFilter("Generos", AnimesOnlineNinjaData.GENRES)
class YearFilter : UriPartFilter("Año", AnimesOnlineNinjaData.YEARS)
class OtherOptionsGroup : AnimeFilter.Group<UriPartFilter>(
"Otros filtros",
listOf(
GenreFilter(),
YearFilter(),
),
)
private inline fun <reified R> AnimeFilter.Group<UriPartFilter>.getItemUri(): String {
return state.first { it is R }.toUriPart()
}
val FILTER_LIST get() = AnimeFilterList(
InvertedResultsFilter(),
TypeFilter(),
LetterFilter(),
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 letter: 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>(),
filters.asUriPart<LetterFilter>(),
others.getItemUri<GenreFilter>(),
others.getItemUri<YearFilter>(),
)
}
private object AnimesOnlineNinjaData {
val EVERY = Pair("Seleccionar", "")
val TYPES = arrayOf(
Pair("Seleccionar", ""),
Pair("Anime", "anime"),
Pair("Peliculas", "pelicula"),
)
val LETTERS = arrayOf(EVERY) + ('a'..'z').map {
Pair(it.toString(), it.toString())
}.toTypedArray()
val GENRES = arrayOf(
EVERY,
Pair("Acción", "accion"),
Pair("Action", "action"),
Pair("Action-Adventure", "action-adventure"),
Pair("Aventura", "aventura"),
Pair("Adventure", "adventure"),
Pair("Animación", "animacion"),
Pair("Animation", "animation"),
Pair("Aventura (Torrent)", "aventura-torrent"),
Pair("Bélica", "belica"),
Pair("Ciencia Ficción", "ciencia-ficcion"),
Pair("Comedia", "comedia"),
Pair("Comedy", "comedy"),
Pair("Crimen", "crimen"),
Pair("Demonios", "demonios"),
Pair("Deportes", "deportes"),
Pair("Documental", "documental"),
Pair("Drama", "drama"),
Pair("Ecchi", "ecchi"),
Pair("En Emisión", "en-emision"),
Pair("Escolares", "escolares"),
Pair("Familia", "familia"),
Pair("Fantasía", "fantasia"),
Pair("Fantasy", "fantasy"),
Pair("Harem", "harem"),
Pair("Historia", "historia"),
Pair("Histórico", "historico"),
Pair("History", "history"),
Pair("Horror", "horror"),
Pair("Kids", "kids"),
Pair("Magia", "magia"),
Pair("Misterio", "misterio"),
Pair("Música", "musica"),
Pair("Parodia", "parodia"),
Pair("Película de TV", "pelicula-de-tv"),
Pair("Reality", "reality"),
Pair("Recuerdos de la Vida", "recuerdos-de-la-vida"),
Pair("Romance", "romance"),
Pair("Sci-Fi Fantasy", "sci-fi-fantasy"),
Pair("Science Fiction", "science-fiction"),
Pair("Seinen", "seinen"),
Pair("Shojo", "shojo"),
Pair("Shounen", "shounen"),
Pair("Soap", "soap"),
Pair("Sobrenatural", "sobrenatural"),
Pair("Suspense", "suspense"),
Pair("Terror", "terror"),
Pair("Thriller", "thriller"),
Pair("War & Politics", "war-politics"),
Pair("Western", "western"),
Pair("Yaoi", "yaoi"),
Pair("Yuri", "yuri"),
)
val YEARS = arrayOf(EVERY) + (2024 downTo 1979).map {
Pair(it.toString(), it.toString())
}.toTypedArray()
}
}