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,17 @@
ext {
extName = 'AnimeBum'
extClass = '.AnimeBum'
extVersionCode = 4
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:universal-extractor'))
implementation(project(':lib:streamhidevid-extractor'))
implementation(project(':lib:vidguard-extractor'))
implementation(project(':lib:filemoon-extractor'))
implementation(project(':lib:gdriveplayer-extractor'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -0,0 +1,356 @@
package eu.kanade.tachiyomi.animeextension.es.animebum
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.gdriveplayerextractor.GdrivePlayerExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.vidguardextractor.VidGuardExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class AnimeBum : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "AnimeBum"
override val baseUrl = "https://www.animebum.net"
override val lang = "es"
override val supportsLatest = false
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request {
return GET("$baseUrl/series?page=$page", headers)
}
override fun popularAnimeSelector(): String = "article.serie"
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
// Extraer el título y enlace
val titleElement = element.selectFirst("div.title h3 a")
anime.title = titleElement?.attr("title") ?: "Sin título"
anime.setUrlWithoutDomain(titleElement?.attr("href") ?: "")
// Extraer la imagen
val imageElement = element.selectFirst("figure.image img")
anime.thumbnail_url = imageElement?.attr("src") ?: ""
return anime
}
override fun popularAnimeNextPageSelector(): String {
return "ul.pagination li a[rel=next]"
}
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request {
throw UnsupportedOperationException()
}
override fun latestUpdatesSelector(): String = popularAnimeSelector()
override fun latestUpdatesFromElement(element: Element): SAnime = popularAnimeFromElement(element)
override fun latestUpdatesNextPageSelector(): String = popularAnimeNextPageSelector()
// =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
return when {
query.isNotBlank() -> GET("$baseUrl/search?s=$query&page=$page", headers)
genreFilter.state != 0 -> GET("$baseUrl/${genreFilter.toUriPart()}?page=$page", headers)
else -> popularAnimeRequest(page)
}
}
override fun searchAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val animes = document.select(searchAnimeSelector()).map { searchAnimeFromElement(it) }
val hasNextPage = searchAnimeNextPageSelector().let { selector ->
document.select(selector).firstOrNull() != null
}
return AnimesPage(animes, hasNextPage)
}
override fun searchAnimeSelector(): String {
return "div.search-results__item"
}
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
val titleElement = element.selectFirst("div.search-results__left a h2")
anime.title = titleElement?.text().orEmpty()
val urlElement = element.selectFirst("div.search-results__left a")
anime.setUrlWithoutDomain(urlElement?.attr("href").orEmpty())
val imgElement = element.selectFirst("div.search-results__img a img")
anime.thumbnail_url = imgElement?.attr("src").orEmpty()
val descriptionElement = element.selectFirst("div.search-results__left div.description")
anime.description = descriptionElement?.text().orEmpty()
return anime
}
override fun searchAnimeNextPageSelector(): String {
return "a.next.page-numbers"
}
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
val synopsisElement = document.selectFirst("div.description p")
anime.description = synopsisElement?.text() ?: "Sin sinopsis"
val yearElement = document.selectFirst("p.datos-serie strong:contains(Año)")
anime.genre = yearElement?.text() ?: ""
// sie es fin o emison la clase
val statusElement = if (document.selectFirst("p.datos-serie strong.emision") != null) {
document.selectFirst("p.datos-serie strong.emision")
} else {
document.selectFirst("p.datos-serie strong.fin")
}
anime.status = parseStatus(statusElement?.text() ?: "")
val genresElement = document.select("div.boom-categories a")
anime.genre = genresElement.joinToString(", ") { it.text() }
return anime
}
private fun parseStatus(status: String): Int {
return when (status) {
"En emisión" -> SAnime.ONGOING
"Finalizado" -> SAnime.COMPLETED
else -> SAnime.UNKNOWN
}
}
// ============================== Episodes ==============================
override fun episodeListSelector(): String {
return "ul.list-episodies li"
}
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
val episodeUrl = element.selectFirst("a")?.attr("href").orEmpty()
val episodeTitle = element.selectFirst("a")?.ownText()?.trim().orEmpty()
val episodeNumber = Regex("""Episodio (\d+)""").find(episodeTitle)?.groupValues?.get(1)?.toFloatOrNull()
episode.setUrlWithoutDomain(episodeUrl)
episode.name = episodeTitle
episode.episode_number = episodeNumber ?: 1F
return episode
}
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
return document.select(episodeListSelector()).map { episodeFromElement(it) }.sortedByDescending { it.episode_number }
}
// ============================ Video Extractor ==========================
private val vidHideExtractor by lazy { StreamHideVidExtractor(client, headers) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
private val vidGuardExtractor by lazy { VidGuardExtractor(client) }
private val gdrivePlayerExtractor by lazy { GdrivePlayerExtractor(client) }
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val videoList = mutableListOf<Video>()
val scriptContent = document.select("script:containsData(var video = [])").firstOrNull()?.data()
?: return videoList
val iframeRegex = """video\[\d+\]\s*=\s*['"]<iframe[^>]+src=["']([^"']+)["']""".toRegex()
val matches = iframeRegex.findAll(scriptContent)
for (match in matches) {
var videoUrl = match.groupValues[1]
if (videoUrl.startsWith("//")) {
videoUrl = "https:$videoUrl"
}
val vidHideDomains = listOf("vidhide", "VidHidePro", "luluvdo", "vidhideplus")
val video = when {
vidHideDomains.any { videoUrl.contains(it, ignoreCase = true) } -> vidHideExtractor.videosFromUrl(videoUrl)
"drive.google" in videoUrl -> {
val newUrl = "https://gdriveplayer.to/embed2.php?link=$videoUrl"
Log.d("AnimeBum", "New URL: $newUrl")
gdrivePlayerExtractor.videosFromUrl(newUrl, "GdrivePlayer", headers)
}
videoUrl.contains("streamwish") -> streamWishExtractor.videosFromUrl(videoUrl)
videoUrl.contains("ok.ru") -> okruExtractor.videosFromUrl(videoUrl)
videoUrl.contains("listeamed") -> vidGuardExtractor.videosFromUrl(videoUrl)
else -> emptyList()
}
videoList.addAll(video)
}
return videoList.sortedByDescending { it.quality }
}
override fun videoListSelector(): String {
throw UnsupportedOperationException()
}
override fun videoFromElement(element: Element): Video {
throw UnsupportedOperationException()
}
override fun videoUrlParse(document: Document): String {
throw UnsupportedOperationException()
}
// ============================ Filters =============================
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
AnimeFilter.Header("La busqueda por texto ignora el filtro"),
GenreFilter(),
)
private class GenreFilter : UriPartFilter(
"Género",
arrayOf(
Pair("<Seleccionar>", ""),
Pair("Acción", "genero/accion"),
Pair("Aventura", "genero/aventura"),
Pair("Ciencia Ficción", "genero/ciencia-ficcion"),
Pair("Comedia", "genero/comedia"),
Pair("Drama", "genero/drama"),
Pair("Terror", "genero/terror"),
Pair("Suspenso", "genero/suspenso"),
Pair("Romance", "genero/romance"),
Pair("Magia", "genero/magia"),
Pair("Misterio", "genero/misterio"),
Pair("Superpoderes", "genero/super-poderes"),
Pair("Shounen", "genero/shounen"),
Pair("Deportes", "genero/deportes"),
Pair("Fantasía", "genero/fantasia"),
Pair("Sobrenatural", "genero/sobrenatural"),
Pair("Música", "genero/musica"),
Pair("Escolares", "genero/escolares"),
Pair("Seinen", "genero/seinen"),
Pair("Histórico", "genero/historico"),
Pair("Psicológico", "genero/psicologico"),
Pair("Mecha", "genero/mecha"),
Pair("Juegos", "genero/juegos"),
Pair("Militar", "genero/militar"),
Pair("Recuentos de la Vida", "genero/recuentos-de-la-vida"),
Pair("Demonios", "genero/demonios"),
Pair("Artes Marciales", "genero/artes-marciales"),
Pair("Espacial", "genero/espacial"),
Pair("Shoujo", "genero/shoujo"),
Pair("Samurái", "genero/samurai"),
Pair("Harem", "genero/harem"),
Pair("Parodia", "genero/parodia"),
Pair("Ecchi", "genero/ecchi"),
Pair("Demencia", "genero/demencia"),
Pair("Vampiros", "genero/vampiros"),
Pair("Josei", "genero/josei"),
Pair("Shounen Ai", "genero/shounen-ai"),
Pair("Shoujo Ai", "genero/shoujo-ai"),
Pair("Latino", "genero/latino"),
Pair("Policía", "genero/policia"),
Pair("Yaoi", "genero/yaoi"),
),
)
private 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
}
// ============================ Preferences =============================
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_DEFAULT = "1080"
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "Voe"
private val SERVER_LIST = arrayOf(
"YourUpload", "BurstCloud", "Voe", "Mp4Upload", "Doodstream",
"Upload", "BurstCloud", "Upstream", "StreamTape", "Amazon",
"Fastream", "Filemoon", "StreamWish", "Okru", "Streamlare",
)
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
return this.sortedWith(
compareBy(
{ it.quality.contains(server, true) },
{ it.quality.contains(quality) },
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
),
).reversed()
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
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)
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = "Preferred quality"
entries = QUALITY_LIST
entryValues = QUALITY_LIST
setDefaultValue(PREF_QUALITY_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)
}
}

View file

@ -0,0 +1,25 @@
ext {
extName = 'Animefenix'
extClass = '.Animefenix'
extVersionCode = 54
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:mp4upload-extractor'))
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:yourupload-extractor'))
implementation(project(':lib:uqload-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:burstcloud-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:filemoon-extractor'))
implementation(project(':lib:voe-extractor'))
implementation(project(':lib:streamlare-extractor'))
implementation(project(':lib:fastream-extractor'))
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:upstream-extractor'))
implementation(project(':lib:streamhidevid-extractor'))
implementation(project(':lib:universal-extractor'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,161 @@
package eu.kanade.tachiyomi.animeextension.es.animefenix
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import java.util.Calendar
object AnimeFenixFilters {
open class QueryPartFilter(displayName: String, val vals: Array<Pair<String, String>>) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray(),
) {
fun toQueryPart(name: String) = vals[state].second.takeIf { it.isNotEmpty() }?.let { "&$name=${vals[state].second}" } ?: run { "" }
}
open class CheckBoxFilterList(name: String, values: List<CheckBox>) : AnimeFilter.Group<AnimeFilter.CheckBox>(name, values)
private class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state)
private inline fun <reified R> AnimeFilterList.parseCheckbox(
options: Array<Pair<String, String>>,
name: String,
): String {
return (this.getFirst<R>() as CheckBoxFilterList).state
.mapNotNull { checkbox ->
if (checkbox.state) {
options.find { it.first == checkbox.name }!!.second
} else {
null
}
}.joinToString("&$name[]=").let {
if (it.isBlank()) {
""
} else {
"&$name[]=$it"
}
}
}
private inline fun <reified R> AnimeFilterList.asQueryPart(name: String): String {
return (this.getFirst<R>() as QueryPartFilter).toQueryPart(name)
}
private inline fun <reified R> AnimeFilterList.getFirst(): R {
return this.filterIsInstance<R>().first()
}
private fun String.changePrefix() = this.takeIf { it.startsWith("&") }?.let { this.replaceFirst("&", "?") } ?: run { this }
data class FilterSearchParams(val filter: String = "") { fun getQuery() = filter.changePrefix() }
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
if (filters.isEmpty()) return FilterSearchParams()
return FilterSearchParams(
filters.parseCheckbox<GenresFilter>(AnimeFenixFiltersData.GENRES, "genero") +
filters.parseCheckbox<YearsFilter>(AnimeFenixFiltersData.YEARS, "year") +
filters.parseCheckbox<TypesFilter>(AnimeFenixFiltersData.TYPES, "type") +
filters.parseCheckbox<StateFilter>(AnimeFenixFiltersData.STATE, "estado") +
filters.asQueryPart<SortFilter>("order"),
)
}
val FILTER_LIST get() = AnimeFilterList(
AnimeFilter.Header("La busqueda por texto ignora el filtro"),
GenresFilter(),
YearsFilter(),
TypesFilter(),
StateFilter(),
SortFilter(),
)
class GenresFilter : CheckBoxFilterList("Género", AnimeFenixFiltersData.GENRES.map { CheckBoxVal(it.first, false) })
class YearsFilter : CheckBoxFilterList("Año", AnimeFenixFiltersData.YEARS.map { CheckBoxVal(it.first, false) })
class TypesFilter : CheckBoxFilterList("Tipo", AnimeFenixFiltersData.TYPES.map { CheckBoxVal(it.first, false) })
class StateFilter : CheckBoxFilterList("Estado", AnimeFenixFiltersData.STATE.map { CheckBoxVal(it.first, false) })
class SortFilter : QueryPartFilter("Orden", AnimeFenixFiltersData.SORT)
private object AnimeFenixFiltersData {
val YEARS = (1990..Calendar.getInstance().get(Calendar.YEAR)).map { Pair("$it", "$it") }.reversed().toTypedArray()
val TYPES = arrayOf(
Pair("TV", "tv"),
Pair("Película", "movie"),
Pair("Especial", "special"),
Pair("OVA", "ova"),
Pair("DONGHUA", "donghua"),
)
val STATE = arrayOf(
Pair("Emisión", "1"),
Pair("Finalizado", "2"),
Pair("Próximamente", "3"),
Pair("En Cuarentena", "4"),
)
val SORT = arrayOf(
Pair("Por Defecto", "default"),
Pair("Recientemente Actualizados", "updated"),
Pair("Recientemente Agregados", "added"),
Pair("Nombre A-Z", "title"),
Pair("Calificación", "likes"),
Pair("Más Vistos", "visits"),
)
val GENRES = arrayOf(
Pair("Acción", "accion"),
Pair("Ángeles", "angeles"),
Pair("Artes Marciales", "artes-marciales"),
Pair("Aventura", "aventura"),
Pair("Ciencia Ficción", "Ciencia Ficción"),
Pair("Comedia", "comedia"),
Pair("Cyberpunk", "cyberpunk"),
Pair("Demonios", "demonios"),
Pair("Deportes", "deportes"),
Pair("Dragones", "dragones"),
Pair("Drama", "drama"),
Pair("Ecchi", "ecchi"),
Pair("Escolares", "escolares"),
Pair("Fantasía", "fantasia"),
Pair("Gore", "gore"),
Pair("Harem", "harem"),
Pair("Histórico", "historico"),
Pair("Horror", "horror"),
Pair("Infantil", "infantil"),
Pair("Isekai", "isekai"),
Pair("Josei", "josei"),
Pair("Juegos", "juegos"),
Pair("Magia", "magia"),
Pair("Mecha", "mecha"),
Pair("Militar", "militar"),
Pair("Misterio", "misterio"),
Pair("Música", "Musica"),
Pair("Ninjas", "ninjas"),
Pair("Parodia", "parodia"),
Pair("Policía", "policia"),
Pair("Psicológico", "psicologico"),
Pair("Recuerdos de la vida", "Recuerdos de la vida"),
Pair("Romance", "romance"),
Pair("Samurai", "samurai"),
Pair("Sci-Fi", "sci-fi"),
Pair("Seinen", "seinen"),
Pair("Shoujo", "shoujo"),
Pair("Shoujo Ai", "shoujo-ai"),
Pair("Shounen", "shounen"),
Pair("Slice of life", "slice-of-life"),
Pair("Sobrenatural", "sobrenatural"),
Pair("Space", "space"),
Pair("Spokon", "spokon"),
Pair("Steampunk", "steampunk"),
Pair("Superpoder", "superpoder"),
Pair("Thriller", "thriller"),
Pair("Vampiro", "vampiro"),
Pair("Yaoi", "yaoi"),
Pair("Yuri", "yuri"),
Pair("Zombies", "zombies"),
)
}
}

View file

@ -0,0 +1,307 @@
package eu.kanade.tachiyomi.animeextension.es.animefenix
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.lib.burstcloudextractor.BurstCloudExtractor
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.net.URLDecoder
class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "AnimeFenix"
override val baseUrl = "https://www3.animefenix.tv"
override val lang = "es"
override val supportsLatest = true
private val preferences: SharedPreferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) }
companion object {
private val SERVER_REGEX = """tabsArray\['?\d+'?]\s*=\s*['\"](https[^'\"]+)['\"]""".toRegex()
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_DEFAULT = "1080"
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "Amazon"
private val SERVER_LIST = arrayOf(
"YourUpload", "Voe", "Mp4Upload", "Doodstream",
"Upload", "BurstCloud", "Upstream", "StreamTape",
"Fastream", "Filemoon", "StreamWish", "Okru",
"Amazon", "AmazonES", "Fireload", "FileLions",
)
}
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/animes?order=likes&page=$page")
override fun popularAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val elements = document.select("div.container .grid.gap-4 a[href]")
val nextPage = document.select("nav[aria-label=Pagination] span:containsOwn(Next)").any()
val animeList = elements.map { element ->
SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href"))
title = element.selectFirst("div h3.text-primary")!!.ownText()
thumbnail_url = element.selectFirst("img.object-cover")?.attr("abs:src")
}
}
return AnimesPage(animeList, nextPage)
}
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/animes?order=added&page=$page")
override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = AnimeFenixFilters.getSearchParameters(filters)
return when {
query.isNotBlank() -> GET("$baseUrl/animes?q=$query&page=$page", headers)
params.filter.isNotBlank() -> GET("$baseUrl/animes${params.getQuery()}&page=$page", headers)
else -> GET("$baseUrl/animes?order=likes&page=$page")
}
}
override fun searchAnimeParse(response: Response) = popularAnimeParse(response)
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
return document.select("div.container > div > ul > li").map { element ->
SEpisode.create().apply {
name = element.selectFirst("span > span")!!.ownText()
setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href"))
}
}
}
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val videoList = mutableListOf<Video>()
val serversData = document.selectFirst("script:containsData(var tabsArray)")?.data() ?: throw Exception("No se encontraron servidores")
val servers = SERVER_REGEX.findAll(serversData).map { it.groupValues[1] }.toList()
servers.parallelForEachBlocking { server ->
val decodedUrl = URLDecoder.decode(server, "UTF-8")
val realUrl = try {
client.newCall(GET(decodedUrl)).execute().asJsoup().selectFirst("script")!!
.data().substringAfter("src=\"").substringBefore("\"")
} catch (e: Exception) { "" }
try {
serverVideoResolver(realUrl).let { videoList.addAll(it) }
} catch (_: Exception) { }
}
return videoList.filter { it.url.contains("https") || it.url.contains("http") }
}
private fun serverVideoResolver(url: String): List<Video> {
val videoList = mutableListOf<Video>()
val embedUrl = url.lowercase()
try {
when {
embedUrl.contains("voe") -> {
VoeExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
(embedUrl.contains("amazon") || embedUrl.contains("amz")) && !embedUrl.contains("disable") -> {
val video = amazonExtractor(baseUrl + url.substringAfter(".."))
if (video.isNotBlank()) {
if (url.contains("&ext=es")) {
videoList.add(Video(video, "AmazonES", video))
} else {
videoList.add(Video(video, "Amazon", video))
}
}
}
embedUrl.contains("ok.ru") || embedUrl.contains("okru") -> {
OkruExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
embedUrl.contains("filemoon") || embedUrl.contains("moonplayer") -> {
val vidHeaders = headers.newBuilder()
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
FilemoonExtractor(client).videosFromUrl(url, prefix = "Filemoon:", headers = vidHeaders).also(videoList::addAll)
}
embedUrl.contains("uqload") -> {
UqloadExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
embedUrl.contains("mp4upload") -> {
Mp4uploadExtractor(client).videosFromUrl(url, headers).let { videoList.addAll(it) }
}
embedUrl.contains("wishembed") || embedUrl.contains("embedwish") || embedUrl.contains("streamwish") || embedUrl.contains("strwish") || embedUrl.contains("wish") -> {
val docHeaders = headers.newBuilder()
.add("Origin", "https://streamwish.to")
.add("Referer", "https://streamwish.to/")
.build()
StreamWishExtractor(client, docHeaders).videosFromUrl(url, videoNameGen = { "StreamWish:$it" }).also(videoList::addAll)
}
embedUrl.contains("doodstream") || embedUrl.contains("dood.") -> {
DoodExtractor(client).videoFromUrl(url, "DoodStream")?.let { videoList.add(it) }
}
embedUrl.contains("streamlare") -> {
StreamlareExtractor(client).videosFromUrl(url).let { videoList.addAll(it) }
}
embedUrl.contains("yourupload") || embedUrl.contains("upload") -> {
YourUploadExtractor(client).videoFromUrl(url, headers = headers).let { videoList.addAll(it) }
}
embedUrl.contains("burstcloud") || embedUrl.contains("burst") -> {
BurstCloudExtractor(client).videoFromUrl(url, headers = headers).let { videoList.addAll(it) }
}
embedUrl.contains("fastream") -> {
FastreamExtractor(client, headers).videosFromUrl(url).also(videoList::addAll)
}
embedUrl.contains("upstream") -> {
UpstreamExtractor(client).videosFromUrl(url).let { videoList.addAll(it) }
}
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> {
StreamTapeExtractor(client).videoFromUrl(url)?.let { videoList.add(it) }
}
embedUrl.contains("ahvsh") || embedUrl.contains("streamhide") -> {
StreamHideVidExtractor(client, headers).videosFromUrl(url).let { videoList.addAll(it) }
}
embedUrl.contains("/stream/fl.php") -> {
val video = url.substringAfter("/stream/fl.php?v=")
if (client.newCall(GET(video)).execute().code == 200) {
videoList.add(Video(video, "FireLoad", video))
}
}
embedUrl.contains("filelions") || embedUrl.contains("lion") -> {
StreamWishExtractor(client, headers).videosFromUrl(url, videoNameGen = { "FileLions:$it" }).also(videoList::addAll)
}
else ->
UniversalExtractor(client).videosFromUrl(url, headers).let { videoList.addAll(it) }
}
} catch (_: Exception) { }
return videoList
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
return this.sortedWith(
compareBy(
{ it.quality.contains(server, true) },
{ it.quality.contains(quality) },
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
),
).reversed()
}
override fun animeDetailsParse(response: Response) = SAnime.create().apply {
val document = response.asJsoup()
with(document.selectFirst("main > div.relative > div.container > div.flex")!!) {
title = selectFirst("h1.font-bold")!!.ownText()
genre = select("div:has(h2:containsOwn(Géneros)) > div.flex > a").joinToString { it.text() }
status = parseStatus(selectFirst("li:has(> span:containsOwn(Estado))")!!.ownText())
description = select("div:has(h2:containsOwn(Sinopsis)) > p").text()
}
}
private fun parseStatus(statusString: String): Int {
return when {
statusString.contains("Emisión") -> SAnime.ONGOING
statusString.contains("Finalizado") -> SAnime.COMPLETED
else -> SAnime.UNKNOWN
}
}
private fun amazonExtractor(url: String): String {
val document = client.newCall(GET(url)).execute().asJsoup()
val videoURl = document.selectFirst("script:containsData(sources: [)")!!.data()
.substringAfter("[{\"file\":\"")
.substringBefore("\",").replace("\\", "")
return try {
if (client.newCall(GET(videoURl)).execute().code == 200) videoURl else ""
} catch (e: Exception) {
""
}
}
override fun getFilterList(): AnimeFilterList = AnimeFenixFilters.FILTER_LIST
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = "Preferred quality"
entries = QUALITY_LIST
entryValues = QUALITY_LIST
setDefaultValue(PREF_QUALITY_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)
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)
}
suspend inline fun <A> Iterable<A>.parallelForEach(crossinline f: suspend (A) -> Unit) {
coroutineScope {
for (item in this@parallelForEach) {
launch(Dispatchers.IO) {
f(item)
}
}
}
}
inline fun <A> Iterable<A>.parallelForEachBlocking(crossinline f: suspend (A) -> Unit) {
runBlocking {
this@parallelForEachBlocking.parallelForEach(f)
}
}
}

View file

@ -0,0 +1,27 @@
package eu.kanade.tachiyomi.animeextension.es.animefenix.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class SolidFilesExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val videoList = mutableListOf<Video>()
return try {
val document = client.newCall(GET(url)).execute().asJsoup()
document.select("script").forEach { script ->
if (script.data().contains("\"downloadUrl\":")) {
val data = script.data().substringAfter("\"downloadUrl\":").substringBefore(",")
val url = data.replace("\"", "")
val videoUrl = url
val quality = prefix + "SolidFiles"
videoList.add(Video(videoUrl, quality, videoUrl))
}
}
videoList
} catch (e: Exception) {
videoList
}
}
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'AnimeFLV'
extClass = '.AnimeFlv'
extVersionCode = 59
extVersionCode = 63
}
apply from: "$rootDir/common.gradle"
@ -11,4 +11,5 @@ dependencies {
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:universal-extractor'))
}

View file

@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
@ -57,7 +58,7 @@ class AnimeFlv : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun popularAnimeSelector(): String = "div.Container ul.ListAnimes li article"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/browse?order=rating&page=$page")
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/browse?order=rating&page=$page", headers)
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
@ -108,18 +109,19 @@ class AnimeFlv : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private val okruExtractor by lazy { OkruExtractor(client) }
private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers.newBuilder().add("Referer", "$baseUrl/").build()) }
private val universalExtractor by lazy { UniversalExtractor(client) }
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val jsonString = document.selectFirst("script:containsData(var videos = {)")?.data() ?: return emptyList()
val responseString = jsonString.substringAfter("var videos =").substringBefore(";").trim()
return json.decodeFromString<ServerModel>(responseString).sub.parallelCatchingFlatMapBlocking {
return json.decodeFromString<ServerModel>(responseString).sub.parallelCatchingFlatMapBlocking { it ->
when (it.title) {
"Stape" -> listOf(streamTapeExtractor.videoFromUrl(it.url ?: it.code)!!)
"Okru" -> okruExtractor.videosFromUrl(it.url ?: it.code)
"YourUpload" -> yourUploadExtractor.videoFromUrl(it.url ?: it.code, headers = headers)
"SW" -> streamWishExtractor.videosFromUrl(it.url ?: it.code, videoNameGen = { "StreamWish:$it" })
else -> emptyList()
else -> universalExtractor.videosFromUrl(it.url ?: it.code, headers)
}
}
}
@ -132,7 +134,6 @@ class AnimeFlv : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = AnimeFlvFilters.getSearchParameters(filters)
return when {
query.isNotBlank() -> GET("$baseUrl/browse?q=$query&page=$page")
params.filter.isNotBlank() -> GET("$baseUrl/browse${params.getQuery()}&page=$page")
@ -166,13 +167,19 @@ class AnimeFlv : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
}
override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector()
override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
override fun latestUpdatesNextPageSelector() = null
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/browse?order=added&page=$page")
override fun latestUpdatesSelector() = "div.Container ul.ListEpisodios li a.fa-play"
override fun latestUpdatesSelector() = popularAnimeSelector()
override fun latestUpdatesFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("a").attr("abs:href").replace("/ver/", "/anime/").substringBeforeLast("-"))
anime.title = element.select("strong.Title").text()
anime.thumbnail_url = element.select("span.Image img").attr("abs:src").replace("thumbs", "covers")
return anime
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!

View file

@ -1,7 +1,7 @@
ext {
extName = 'AnimeID'
extClass = '.AnimeID'
extVersionCode = 10
extVersionCode = 15
}
apply from: "$rootDir/common.gradle"
@ -9,4 +9,5 @@ apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:universal-extractor'))
}

View file

@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
@ -120,6 +121,7 @@ class AnimeID : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// ============================ Video Links =============================
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
private val universalExtractor by lazy { UniversalExtractor(client) }
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
@ -128,11 +130,10 @@ class AnimeID : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val jsonString = script.attr("data")
val jsonUnescape = unescapeJava(jsonString)!!.replace("\\", "")
val url = fetchUrls(jsonUnescape).firstOrNull()?.replace("\\\\", "\\") ?: ""
if (url.contains("streamtape") || url.contains("tape") || url.contains("stp")) {
streamtapeExtractor.videosFromUrl(url).also(videoList::addAll)
}
if (url.contains("wish") || url.contains("fviplions") || url.contains("obeywish")) {
streamwishExtractor.videosFromUrl(url, videoNameGen = { "StreamWish:$it" }).also(videoList::addAll)
return when {
url.contains("streamtape") || url.contains("tape") || url.contains("stp") -> streamtapeExtractor.videosFromUrl(url)
url.contains("wish") || url.contains("fviplions") || url.contains("obeywish") -> streamwishExtractor.videosFromUrl(url, videoNameGen = { "StreamWish:$it" })
else -> universalExtractor.videosFromUrl(url, headers)
}
}
return videoList

View file

@ -0,0 +1,20 @@
ext {
extName = 'Animejl'
extClass = '.Animejl'
extVersionCode = 4
isNsfw = true
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:yourupload-extractor'))
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:voe-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:streamhidevid-extractor'))
implementation(project(':lib:universal-extractor'))
implementation(project(':lib:uqload-extractor'))
implementation(project(':lib:mp4upload-extractor'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -0,0 +1,244 @@
package eu.kanade.tachiyomi.animeextension.es.animejl
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.Exception
class Animejl : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Animejl"
override val baseUrl = "https://www.anime-jl.net"
override val lang = "es"
override val supportsLatest = true
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_DEFAULT = "720"
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "StreamWish"
private val SERVER_LIST = arrayOf("StreamWish", "YourUpload", "Okru", "StreamTape", "StreamHideVid", "Voe", "Uqload", "Mp4upload")
}
override fun popularAnimeSelector(): String = "div.Container ul.ListAnimes li article"
override fun popularAnimeRequest(page: Int): Request =
GET("$baseUrl/animes?order=rating&page=$page")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("div.Description a.Button").attr("abs:href"))
anime.title = element.select("a h3").text()
anime.thumbnail_url = element.select("a div.Image figure img").attr("src").replace("/storage", "$baseUrl/storage")
anime.description = element.select("div.Description p:eq(2)").text().removeSurrounding("\"")
return anime
}
override fun popularAnimeNextPageSelector(): String = "ul.pagination li a[rel=\"next\"]"
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val episodeList = mutableListOf<SEpisode>()
val script = document.select("script:containsData(var episodes =)").firstOrNull()?.data() ?: return emptyList()
val episodesPattern = Regex("var episodes = (\\[.*?\\]);", RegexOption.DOT_MATCHES_ALL)
val episodesMatch = episodesPattern.find(script) ?: return emptyList()
val episodesString = episodesMatch.groupValues[1]
val animeInfoPattern = Regex("var anime_info = \\[(.*?)\\];")
val animeInfoMatch = animeInfoPattern.find(script) ?: return emptyList()
val animeInfo = animeInfoMatch.groupValues[1].split(",").map { it.trim('"') }
val animeSlug = animeInfo.getOrNull(2) ?: ""
val animeId = animeInfo.getOrNull(0) ?: ""
val episodePattern = Regex("\\[(\\d+),\"(.*?)\",\"(.*?)\",\"(.*?)\"\\]")
val episodeMatches = episodePattern.findAll(episodesString)
episodeMatches.forEach { match ->
try {
val episodeNumber = match.groupValues[1].toIntOrNull() ?: 0
val url = "$baseUrl/anime/$animeId/$animeSlug/episodio-$episodeNumber"
val episode = SEpisode.create()
episode.setUrlWithoutDomain(url)
episode.episode_number = episodeNumber.toFloat()
episode.name = "Episodio $episodeNumber"
episodeList.add(episode)
} catch (e: Exception) {
Log.e("Animejl", "Error processing episode: ${e.message}")
}
}
return episodeList.sortedByDescending { it.episode_number }
}
override fun episodeListSelector() = "uwu"
override fun episodeFromElement(element: Element) = throw UnsupportedOperationException()
/*--------------------------------Video extractors------------------------------------*/
private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
private val universalExtractor by lazy { UniversalExtractor(client) }
private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client, headers) }
private val voeExtractor by lazy { VoeExtractor(client) }
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val scriptContent = document.selectFirst("script:containsData(var video = [)")?.data()
?: return emptyList()
val videoList = mutableListOf<Video>()
val videoPattern = Regex("""video\[\d+\] = '<iframe src="(.*?)"""")
val matches = videoPattern.findAll(scriptContent)
matches.forEach { match ->
val url = match.groupValues[1]
val videos = when {
url.contains("streamtape") -> listOfNotNull(streamTapeExtractor.videoFromUrl(url))
url.contains("ok.ru") -> okruExtractor.videosFromUrl(url)
url.contains("yourupload") -> yourUploadExtractor.videoFromUrl(url, headers)
url.contains("streamwish") || url.contains("playerwish") -> streamWishExtractor.videosFromUrl(url)
url.contains("streamhidevid") -> streamHideVidExtractor.videosFromUrl(url)
url.contains("voe") -> voeExtractor.videosFromUrl(url)
url.contains("uqload") -> uqloadExtractor.videosFromUrl(url)
url.contains("mp4upload") -> mp4uploadExtractor.videosFromUrl(url, headers)
else -> universalExtractor.videosFromUrl(url, headers)
}
videoList.addAll(videos)
}
return videoList.sort()
}
override fun videoListSelector() = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = AnimejlFilters.getSearchParameters(filters)
return when {
query.isNotBlank() -> GET("$baseUrl/animes?q=$query&page=$page")
params.filter.isNotBlank() -> GET("$baseUrl/animes${params.getQuery()}&page=$page")
else -> popularAnimeRequest(page)
}
}
override fun getFilterList(): AnimeFilterList = AnimejlFilters.FILTER_LIST
override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
override fun searchAnimeSelector(): String = popularAnimeSelector()
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url =
document.selectFirst("div.AnimeCover div.Image figure img")!!.attr("abs:src")
anime.title = document.selectFirst("div.Ficha.fchlt div.Container .Title")!!.text()
anime.description = document.selectFirst("div.Description")!!.text().removeSurrounding("\"")
anime.genre = document.select("nav.Nvgnrs a").joinToString { it.text() }
anime.status = parseStatus(document.select("span.fa-tv").text())
return anime
}
private fun parseStatus(statusString: String): Int {
return when {
statusString.contains("En emision") -> SAnime.ONGOING
statusString.contains("Finalizado") -> SAnime.COMPLETED
else -> SAnime.UNKNOWN
}
}
override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector()
override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/animes?order=updated&page=$page")
override fun latestUpdatesSelector() = popularAnimeSelector()
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
return this.sortedWith(
compareBy(
{ it.quality.contains(server, true) },
{ it.quality.contains(quality) },
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
),
).reversed()
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
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)
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = "Preferred quality"
entries = QUALITY_LIST
entryValues = QUALITY_LIST
setDefaultValue(PREF_QUALITY_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)
}
}

View file

@ -0,0 +1,159 @@
package eu.kanade.tachiyomi.animeextension.es.animejl
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import java.util.Calendar
object AnimejlFilters {
open class QueryPartFilter(displayName: String, val vals: Array<Pair<String, String>>) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray(),
) {
fun toQueryPart(name: String) = vals[state].second.takeIf { it.isNotEmpty() }?.let { "&$name=${vals[state].second}" } ?: run { "" }
}
open class CheckBoxFilterList(name: String, values: List<CheckBox>) : AnimeFilter.Group<AnimeFilter.CheckBox>(name, values)
private class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state)
private inline fun <reified R> AnimeFilterList.parseCheckbox(
options: Array<Pair<String, String>>,
name: String,
): String {
return (this.getFirst<R>() as CheckBoxFilterList).state
.mapNotNull { checkbox ->
if (checkbox.state) {
options.find { it.first == checkbox.name }!!.second
} else {
null
}
}.joinToString("&$name[]=").let {
if (it.isBlank()) {
""
} else {
"&$name[]=$it"
}
}
}
private inline fun <reified R> AnimeFilterList.asQueryPart(name: String): String {
return (this.getFirst<R>() as QueryPartFilter).toQueryPart(name)
}
private inline fun <reified R> AnimeFilterList.getFirst(): R {
return this.filterIsInstance<R>().first()
}
private fun String.changePrefix() = this.takeIf { it.startsWith("&") }?.let { this.replaceFirst("&", "?") } ?: run { this }
data class FilterSearchParams(val filter: String = "") { fun getQuery() = filter.changePrefix() }
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
if (filters.isEmpty()) return FilterSearchParams()
return FilterSearchParams(
filters.parseCheckbox<GenresFilter>(AnimeFlvFiltersData.GENRES, "genre") +
filters.parseCheckbox<YearsFilter>(AnimeFlvFiltersData.YEARS, "year") +
filters.parseCheckbox<TypesFilter>(AnimeFlvFiltersData.TYPES, "type") +
filters.parseCheckbox<StateFilter>(AnimeFlvFiltersData.STATE, "estado") +
filters.asQueryPart<SortFilter>("order"),
)
}
val FILTER_LIST get() = AnimeFilterList(
AnimeFilter.Header("La busqueda por texto ignora el filtro"),
GenresFilter(),
YearsFilter(),
TypesFilter(),
StateFilter(),
SortFilter(),
)
class GenresFilter : CheckBoxFilterList("Género", AnimeFlvFiltersData.GENRES.map { CheckBoxVal(it.first, false) })
class YearsFilter : CheckBoxFilterList("Año", AnimeFlvFiltersData.YEARS.map { CheckBoxVal(it.first, false) })
class TypesFilter : CheckBoxFilterList("Tipo", AnimeFlvFiltersData.TYPES.map { CheckBoxVal(it.first, false) })
class StateFilter : CheckBoxFilterList("Estado", AnimeFlvFiltersData.STATE.map { CheckBoxVal(it.first, false) })
class SortFilter : QueryPartFilter("Orden", AnimeFlvFiltersData.SORT)
private object AnimeFlvFiltersData {
val YEARS = (1990..Calendar.getInstance().get(Calendar.YEAR)).map { Pair("$it", "$it") }.reversed().toTypedArray()
val TYPES = arrayOf(
Pair("Anime", "1"),
Pair("Ova", "2"),
Pair("Pelicula", "3"),
Pair("Donghua", "7"),
)
val STATE = arrayOf(
Pair("En emisión", "0"),
Pair("Finalizado", "1"),
Pair("Próximamente", "2"),
)
val SORT = arrayOf(
Pair("Por Defecto", "created"),
Pair("Recientemente Actualizados", "updated"),
Pair("Nombre A-Z", "titleaz"),
Pair("Nombre Z-A", "titleza"),
Pair("Calificación", "rating"),
Pair("Vistas", "views"),
)
val GENRES = arrayOf(
Pair("Acción", "1"),
Pair("Artes Marciales", "2"),
Pair("Aventuras", "3"),
Pair("Ciencia Ficción", "33"),
Pair("Comedia", "9"),
Pair("Cultivación", "71"),
Pair("Demencia", "40"),
Pair("Demonios", "42"),
Pair("Deportes", "27"),
Pair("Donghua", "50"),
Pair("Drama", "10"),
Pair("Ecchi", "25"),
Pair("Escolares", "22"),
Pair("Espacial", "48"),
Pair("Fantasia", "6"),
Pair("Gore", "67"),
Pair("Harem", "32"),
Pair("Hentai", "31"),
Pair("Historico", "43"),
Pair("Horror", "39"),
Pair("Isekai", "45"),
Pair("Josei", "70"),
Pair("Juegos", "11"),
Pair("Latino / Castellano", "46"),
Pair("Magia", "38"),
Pair("Mecha", "41"),
Pair("Militar", "44"),
Pair("Misterio", "26"),
Pair("Mitología", "73"),
Pair("Musica", "28"),
Pair("Parodia", "13"),
Pair("Policía", "51"),
Pair("Psicologico", "29"),
Pair("Recuentos de la vida", "23"),
Pair("Reencarnación", "72"),
Pair("Romance", "12"),
Pair("Samurai", "69"),
Pair("Seinen", "24"),
Pair("Shoujo", "36"),
Pair("Shounen", "4"),
Pair("Sin Censura", "68"),
Pair("Sobrenatural", "7"),
Pair("Superpoderes", "5"),
Pair("Suspenso", "21"),
Pair("Terror", "20"),
Pair("Vampiros", "49"),
Pair("Venganza", "74"),
Pair("Yaoi", "53"),
Pair("Yuri", "52"),
)
}
}

View file

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

View file

@ -85,21 +85,21 @@ class AnimeLatinoHD : ConfigurableAnimeSource, AnimeHttpSource() {
if (url.contains("status=1")) {
val latestData = data["data"]!!.jsonArray
latestData.forEach { item ->
val animeItem = item!!.jsonObject
val animeItem = item.jsonObject
val anime = SAnime.create()
anime.setUrlWithoutDomain(externalOrInternalImg("anime/${animeItem["slug"]!!.jsonPrimitive!!.content}"))
anime.thumbnail_url = "https://image.tmdb.org/t/p/w200${animeItem["poster"]!!.jsonPrimitive!!.content}"
anime.title = animeItem["name"]!!.jsonPrimitive!!.content
anime.setUrlWithoutDomain(externalOrInternalImg("anime/${animeItem["slug"]!!.jsonPrimitive.content}"))
anime.thumbnail_url = "https://image.tmdb.org/t/p/w200${animeItem["poster"]!!.jsonPrimitive.content}"
anime.title = animeItem["name"]!!.jsonPrimitive.content
animeList.add(anime)
}
} else {
val popularToday = data["popular_today"]!!.jsonArray
popularToday.forEach { item ->
val animeItem = item!!.jsonObject
val animeItem = item.jsonObject
val anime = SAnime.create()
anime.setUrlWithoutDomain(externalOrInternalImg("anime/${animeItem["slug"]!!.jsonPrimitive!!.content}"))
anime.thumbnail_url = "https://image.tmdb.org/t/p/w200${animeItem["poster"]!!.jsonPrimitive!!.content}"
anime.title = animeItem["name"]!!.jsonPrimitive!!.content
anime.setUrlWithoutDomain(externalOrInternalImg("anime/${animeItem["slug"]!!.jsonPrimitive.content}"))
anime.thumbnail_url = "https://image.tmdb.org/t/p/w200${animeItem["poster"]!!.jsonPrimitive.content}"
anime.title = animeItem["name"]!!.jsonPrimitive.content
animeList.add(anime)
}
}
@ -122,12 +122,12 @@ class AnimeLatinoHD : ConfigurableAnimeSource, AnimeHttpSource() {
val pageProps = props["pageProps"]!!.jsonObject
val data = pageProps["data"]!!.jsonObject
newAnime.title = data["name"]!!.jsonPrimitive!!.content
newAnime.genre = data["genres"]!!.jsonPrimitive!!.content.split(",").joinToString()
newAnime.description = data["overview"]!!.jsonPrimitive!!.content
newAnime.status = parseStatus(data["status"]!!.jsonPrimitive!!.content)
newAnime.thumbnail_url = "https://image.tmdb.org/t/p/w600_and_h900_bestv2${data["poster"]!!.jsonPrimitive!!.content}"
newAnime.setUrlWithoutDomain(externalOrInternalImg("anime/${data["slug"]!!.jsonPrimitive!!.content}"))
newAnime.title = data["name"]!!.jsonPrimitive.content
newAnime.genre = data["genres"]!!.jsonPrimitive.content.split(",").joinToString()
newAnime.description = data["overview"]!!.jsonPrimitive.content
newAnime.status = parseStatus(data["status"]!!.jsonPrimitive.content)
newAnime.thumbnail_url = "https://image.tmdb.org/t/p/w600_and_h900_bestv2${data["poster"]!!.jsonPrimitive.content}"
newAnime.setUrlWithoutDomain(externalOrInternalImg("anime/${data["slug"]!!.jsonPrimitive.content}"))
}
}
return newAnime
@ -144,11 +144,11 @@ class AnimeLatinoHD : ConfigurableAnimeSource, AnimeHttpSource() {
val data = pageProps["data"]!!.jsonObject
val arrEpisode = data["episodes"]!!.jsonArray
arrEpisode.forEach { item ->
val animeItem = item!!.jsonObject
val animeItem = item.jsonObject
val episode = SEpisode.create()
episode.setUrlWithoutDomain(externalOrInternalImg("ver/${data["slug"]!!.jsonPrimitive!!.content}/${animeItem["number"]!!.jsonPrimitive!!.content!!.toFloat()}"))
episode.episode_number = animeItem["number"]!!.jsonPrimitive!!.content!!.toFloat()
episode.name = "Episodio ${animeItem["number"]!!.jsonPrimitive!!.content!!.toFloat()}"
episode.setUrlWithoutDomain(externalOrInternalImg("ver/${data["slug"]!!.jsonPrimitive.content}/${animeItem["number"]!!.jsonPrimitive.content.toFloat()}"))
episode.episode_number = animeItem["number"]!!.jsonPrimitive.content.toFloat()
episode.name = "Episodio ${animeItem["number"]!!.jsonPrimitive.content.toFloat()}"
episodeList.add(episode)
}
}
@ -158,7 +158,7 @@ class AnimeLatinoHD : ConfigurableAnimeSource, AnimeHttpSource() {
private fun parseJsonArray(json: JsonElement?): List<JsonElement> {
val list = mutableListOf<JsonElement>()
json!!.jsonObject!!.entries!!.forEach { list.add(it.value) }
json!!.jsonObject.entries.forEach { list.add(it.value) }
return list
}
@ -178,11 +178,11 @@ class AnimeLatinoHD : ConfigurableAnimeSource, AnimeHttpSource() {
val pageProps = props["pageProps"]!!.jsonObject
val data = pageProps["data"]!!.jsonObject
val playersElement = data["players"]
val players = if (playersElement !is JsonArray) JsonArray(parseJsonArray(playersElement)) else playersElement!!.jsonArray
val players = if (playersElement !is JsonArray) JsonArray(parseJsonArray(playersElement)) else playersElement.jsonArray
players.forEach { player ->
val servers = player!!.jsonArray
val servers = player.jsonArray
servers.forEach { server ->
val item = server!!.jsonObject
val item = server.jsonObject
val request = client.newCall(
GET(
url = "https://api.animelatinohd.com/stream/${item["id"]!!.jsonPrimitive.content}",
@ -193,9 +193,9 @@ class AnimeLatinoHD : ConfigurableAnimeSource, AnimeHttpSource() {
.build(),
),
).execute()
val locationsDdh = request!!.networkResponse.toString()
val locationsDdh = request.networkResponse.toString()
fetchUrls(locationsDdh).map { url ->
val language = if (item["languaje"]!!.jsonPrimitive!!.content == "1") "[LAT]" else "[SUB]"
val language = if (item["languaje"]!!.jsonPrimitive.content == "1") "[LAT]" else "[SUB]"
val embedUrl = url.lowercase()
if (embedUrl.contains("filemoon")) {
val vidHeaders = headers.newBuilder()
@ -211,7 +211,7 @@ class AnimeLatinoHD : ConfigurableAnimeSource, AnimeHttpSource() {
StreamTapeExtractor(client).videoFromUrl(url, "$language Streamtape")?.let { videoList.add(it) }
}
if (embedUrl.contains("dood")) {
DoodExtractor(client).videoFromUrl(url, "$language DoodStream")?.let { videoList.add(it) }
DoodExtractor(client).videoFromUrl(url, language)?.let { videoList.add(it) }
}
if (embedUrl.contains("okru") || embedUrl.contains("ok.ru")) {
OkruExtractor(client).videosFromUrl(url, language).also(videoList::addAll)
@ -281,11 +281,11 @@ class AnimeLatinoHD : ConfigurableAnimeSource, AnimeHttpSource() {
val data = pageProps["data"]!!.jsonObject
val arrData = data["data"]!!.jsonArray
arrData.forEach { item ->
val animeItem = item!!.jsonObject
val animeItem = item.jsonObject
val anime = SAnime.create()
anime.setUrlWithoutDomain(externalOrInternalImg("anime/${animeItem["slug"]!!.jsonPrimitive!!.content}"))
anime.thumbnail_url = "https://image.tmdb.org/t/p/w200${animeItem["poster"]!!.jsonPrimitive!!.content}"
anime.title = animeItem["name"]!!.jsonPrimitive!!.content
anime.setUrlWithoutDomain(externalOrInternalImg("anime/${animeItem["slug"]!!.jsonPrimitive.content}"))
anime.thumbnail_url = "https://image.tmdb.org/t/p/w200${animeItem["poster"]!!.jsonPrimitive.content}"
anime.title = animeItem["name"]!!.jsonPrimitive.content
animeList.add(anime)
}
}

View file

@ -31,28 +31,32 @@ class JsUnpacker(packedJS: String?) {
Pattern.compile("""\}\s*\('(.*)',\s*(.*?),\s*(\d+),\s*'(.*?)'\.split\('\|'\)""", Pattern.DOTALL)
var m = p.matcher(js)
if (m.find() && m.groupCount() == 4) {
val payload = m.group(1).replace("\\'", "'")
val payload = m.group(1)?.replace("\\'", "'")
val radixStr = m.group(2)
val countStr = m.group(3)
val symtab = m.group(4).split("\\|".toRegex()).toTypedArray()
val radix = radixStr.toIntOrNull() ?: 36
val count = countStr.toIntOrNull() ?: 0
if (symtab.size != count) {
throw Exception("Unknown p.a.c.k.e.r. encoding")
val symtab = m.group(4)?.split("\\|".toRegex())?.toTypedArray()
val radix = radixStr?.toIntOrNull() ?: 36
val count = countStr?.toIntOrNull() ?: 0
if (symtab != null) {
if (symtab.size != count) {
throw Exception("Unknown p.a.c.k.e.r. encoding")
}
}
val unbase = Unbase(radix)
p = Pattern.compile("\\b\\w+\\b")
m = p.matcher(payload)
m = payload?.let { p.matcher(it) }!!
val decoded = StringBuilder(payload)
var replaceOffset = 0
while (m.find()) {
val word = m.group(0)
val x = unbase.unbase(word)
var value: String? = null
if (x < symtab.size && x >= 0) {
value = symtab[x]
if (symtab != null) {
if (x < symtab.size && x >= 0) {
value = symtab[x]
}
}
if (value != null && value.isNotEmpty()) {
if (!value.isNullOrEmpty()) {
decoded.replace(m.start() + replaceOffset, m.end() + replaceOffset, value)
replaceOffset += value.length - word.length
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'AnimeMovil'
extClass = '.AnimeMovil'
extVersionCode = 17
extVersionCode = 27
}
apply from: "$rootDir/common.gradle"
@ -20,4 +20,5 @@ dependencies {
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:playlist-utils'))
implementation(project(':lib:streamlare-extractor'))
implementation(project(':lib:universal-extractor'))
}

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.animeextension.es.animemovil
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
@ -20,6 +21,7 @@ import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
@ -233,50 +235,57 @@ class AnimeMovil : ConfigurableAnimeSource, AnimeHttpSource() {
val videoList = mutableListOf<Video>()
val embedUrl = url.lowercase()
try {
if (embedUrl.contains("voe")) {
VoeExtractor(client).videosFromUrl(url).also(videoList::addAll)
return when {
embedUrl.contains("voe") -> {
VoeExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
embedUrl.contains("filemoon") || embedUrl.contains("moonplayer") -> {
FilemoonExtractor(client).videosFromUrl(url, prefix = "Filemoon:").also(videoList::addAll)
}
embedUrl.contains("uqload") -> {
UqloadExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
embedUrl.contains("mp4upload") -> {
val newHeaders = headers.newBuilder().add("referer", "https://re.animepelix.net/").build()
Mp4uploadExtractor(client).videosFromUrl(url, newHeaders).also(videoList::addAll)
}
embedUrl.contains("wishembed") || embedUrl.contains("streamwish") || embedUrl.contains("wish") -> {
val docHeaders = headers.newBuilder()
.add("Referer", "$baseUrl/")
.build()
StreamWishExtractor(client, docHeaders).videosFromUrl(url, videoNameGen = { "StreamWish:$it" }).also(videoList::addAll)
}
embedUrl.contains("doodstream") || embedUrl.contains("dood.") -> {
DoodExtractor(client).videosFromUrl(url, "DoodStream").also(videoList::addAll)
}
embedUrl.contains("streamlare") -> {
StreamlareExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
embedUrl.contains("yourupload") -> {
YourUploadExtractor(client).videoFromUrl(url, headers = headers).also(videoList::addAll)
}
embedUrl.contains("burstcloud") || embedUrl.contains("burst") -> {
BurstCloudExtractor(client).videoFromUrl(url, headers = headers).also(videoList::addAll)
}
embedUrl.contains("fastream") -> {
FastreamExtractor(client, headers).videosFromUrl(url).also(videoList::addAll)
}
embedUrl.contains("upstream") -> {
UpstreamExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
embedUrl.contains("streamtape") -> {
StreamTapeExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
embedUrl.contains("filelions") || embedUrl.contains("lion") -> {
StreamWishExtractor(client, headers).videosFromUrl(url, videoNameGen = { "FileLions:$it" }).also(videoList::addAll)
}
else -> {
UniversalExtractor(client).videosFromUrl(url, headers).also(videoList::addAll)
}
}
if (embedUrl.contains("filemoon") || embedUrl.contains("moonplayer")) {
FilemoonExtractor(client).videosFromUrl(url, prefix = "Filemoon:").also(videoList::addAll)
}
if (embedUrl.contains("uqload")) {
UqloadExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
if (embedUrl.contains("mp4upload")) {
val newHeaders = headers.newBuilder().add("referer", "https://re.animepelix.net/").build()
Mp4uploadExtractor(client).videosFromUrl(url, newHeaders).also(videoList::addAll)
}
if (embedUrl.contains("wishembed") || embedUrl.contains("streamwish") || embedUrl.contains("wish")) {
val docHeaders = headers.newBuilder()
.add("Referer", "$baseUrl/")
.build()
StreamWishExtractor(client, docHeaders).videosFromUrl(url, videoNameGen = { "StreamWish:$it" }).also(videoList::addAll)
}
if (embedUrl.contains("doodstream") || embedUrl.contains("dood.")) {
DoodExtractor(client).videoFromUrl(url, "DoodStream", false)?.let { videoList.add(it) }
}
if (embedUrl.contains("streamlare")) {
StreamlareExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
if (embedUrl.contains("yourupload")) {
YourUploadExtractor(client).videoFromUrl(url, headers = headers).also(videoList::addAll)
}
if (embedUrl.contains("burstcloud") || embedUrl.contains("burst")) {
BurstCloudExtractor(client).videoFromUrl(url, headers = headers).also(videoList::addAll)
}
if (embedUrl.contains("fastream")) {
FastreamExtractor(client, headers).videosFromUrl(url).also(videoList::addAll)
}
if (embedUrl.contains("upstream")) {
UpstreamExtractor(client).videosFromUrl(url).also(videoList::addAll)
}
if (embedUrl.contains("streamtape")) {
StreamTapeExtractor(client).videoFromUrl(url)?.also(videoList::add)
}
if (embedUrl.contains("filelions") || embedUrl.contains("lion")) {
StreamWishExtractor(client, headers).videosFromUrl(url, videoNameGen = { "FileLions:$it" }).also(videoList::addAll)
}
} catch (_: Exception) {}
} catch (_: Exception) {
Log.e("AnimeMovil", "Error: Server not supported")
}
return videoList
}

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()
}
}

View file

@ -2,8 +2,8 @@ ext {
extName = 'AnimeYT.es'
extClass = '.AnimeYTES'
themePkg = 'animestream'
baseUrl = 'https://animeyt.es'
overrideVersionCode = 3
baseUrl = 'https://animeyt.pro'
overrideVersionCode = 8
}
apply from: "$rootDir/common.gradle"
@ -12,4 +12,8 @@ dependencies {
implementation(project(":lib:okru-extractor"))
implementation(project(":lib:streamtape-extractor"))
implementation(project(":lib:sendvid-extractor"))
implementation(project(":lib:yourupload-extractor"))
implementation(project(":lib:burstcloud-extractor"))
implementation(project(":lib:filemoon-extractor"))
implementation(project(":lib:universal-extractor"))
}

View file

@ -1,29 +1,111 @@
package eu.kanade.tachiyomi.animeextension.es.animeytes
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.burstcloudextractor.BurstCloudExtractor
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.sendvidextractor.SendvidExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class AnimeYTES : AnimeStream(
"es",
"AnimeYT.es",
"https://animeyt.es",
) {
override val preferences: SharedPreferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) }
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_DEFAULT = "1080"
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "Amazon"
private val SERVER_LIST = arrayOf(
"YourUpload",
"SendVid",
"BurstCloud",
"StreamTape",
"Filemoon",
"Okru",
)
}
override val animeListUrl = "$baseUrl/tv"
// ============================ Video Links =============================
private val okruExtractor by lazy { OkruExtractor(client) }
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
private val sendvidExtractor by lazy { SendvidExtractor(client, headers) }
private val youruploadExtractor by lazy { YourUploadExtractor(client) }
private val burstcloudExtractor by lazy { BurstCloudExtractor(client) }
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
private val universalExtractor by lazy { UniversalExtractor(client) }
override fun getVideoList(url: String, name: String): List<Video> {
return when (name) {
"OK" -> okruExtractor.videosFromUrl(url)
"Stream" -> streamtapeExtractor.videosFromUrl(url)
"Send" -> sendvidExtractor.videosFromUrl(url)
else -> emptyList()
"Your" -> youruploadExtractor.videoFromUrl(url, headers)
"Alpha" -> burstcloudExtractor.videoFromUrl(url, headers)
"Moon" -> filemoonExtractor.videosFromUrl(url)
else -> universalExtractor.videosFromUrl(url, headers)
}
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
return this.sortedWith(
compareBy(
{ it.quality.contains(server, true) },
{ it.quality.contains(quality) },
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
),
).reversed()
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = "Preferred quality"
entries = QUALITY_LIST
entryValues = QUALITY_LIST
setDefaultValue(PREF_QUALITY_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)
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)
}
}

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.azanimex.AzanimexUrlActivity"
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="www.az-animex.com"
android:pathPattern="/anime/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,7 @@
ext {
extName = 'az-animex'
extClass = '.Azanimex'
extVersionCode = 2
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,242 @@
package eu.kanade.tachiyomi.animeextension.es.azanimex
import android.util.Log
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.net.URLEncoder
class Azanimex : ParsedAnimeHttpSource() {
override val name = "az-animex"
override val baseUrl = "https://www.az-animex.com"
override val lang = "es"
override val supportsLatest = false
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request {
val url = if (page > 1) "$baseUrl/?query-22-page=$page" else baseUrl
return GET(url)
}
override fun popularAnimeSelector(): String = "li.wp-block-post"
override fun popularAnimeFromElement(element: Element): SAnime {
return SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("h2.wp-block-post-title a")?.attr("href") ?: "")
title =
element.selectFirst("h2.wp-block-post-title a")?.text()?.substringBefore("[") ?: ""
thumbnail_url =
element.selectFirst("figure.wp-block-post-featured-image img")?.attr("data-src")
val genres = mutableListOf<String>()
element.select("div[class*=taxonomy-genero] a").forEach { genres.add(it.text()) }
element.select("div[class*=taxonomy-tipo] a")
.forEach { genres.add("Tipo: ${it.text()}") }
genre = genres.joinToString(", ")
}
}
override fun popularAnimeNextPageSelector(): String = "a.page-numbers:not(.prev):not(.next)"
// ============================== Episodes ==============================
override fun episodeListSelector() = throw Exception("Not used")
override fun episodeFromElement(element: Element): SEpisode {
throw Exception("Not used")
}
private fun getPathFromUrl(url: String): String {
val cleanUrl = url.replace("https://", "").replace("http://", "")
return cleanUrl.substringAfter("/").replace("//", "/").replaceFirst("es/", "")
}
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val mainUrl = document.select("a.su-button[href*='.az-animex.com']").firstOrNull()
?.attr("href") ?: throw Exception("No se encontró la URL de los episodios")
val updatedUrl = updateDomainInUrl(mainUrl)
val path = getPathFromUrl(updatedUrl)
val url1 = updatedUrl.substringAfter("https://").substringBefore("/")
val apiUrl = "https://$url1/api?path=$path"
val apiResponse = client.newCall(
GET(apiUrl, headers = headers),
).execute()
return try {
val json = Json { ignoreUnknownKeys = true }
val responseBody = apiResponse.body.string()
val result = json.decodeFromString<OneDriveResponse>(responseBody)
val episodes = mutableListOf<SEpisode>()
result.file?.let { file ->
if (file.name.endsWith(".mp4")) {
val episode = SEpisode.create().apply {
val animeurl = "https://$url1/api/raw/?path=$path"
name = file.name.substringAfter("] ").substringBeforeLast(" [")
episode_number = parseEpisodeNumber(file.name)
Log.d("Azanimex", "URL del anime: $animeurl")
url = animeurl
}
episodes.add(episode)
}
}
result.folder?.value?.forEach { file ->
if (file.name.endsWith(".mp4")) {
val episode = SEpisode.create().apply {
val fileUrl = URLEncoder.encode(file.name, "UTF-8")
val animeurl = "https://$url1/api/raw/?path=$path/$fileUrl"
// Ajuste para extraer correctamente el nombre del episodio
name = file.name.substringAfter("] ").substringBeforeLast(" [")
episode_number = parseEpisodeNumber(file.name)
Log.d("Azanimex", "URL del anime: $animeurl")
url = animeurl
}
episodes.add(episode)
}
}
episodes.sortedByDescending { it.name }
} catch (e: Exception) {
throw Exception("Error al procesar los episodios: ${e.message}, Respuesta JSON: ")
}
}
private fun parseEpisodeNumber(filename: String): Float {
val regex = Regex("""-(\d+)\s*\[""")
return regex.find(filename)?.groupValues?.get(1)?.toFloatOrNull() ?: 0f
}
private fun updateDomainInUrl(url: String): String {
return when {
url.contains("series-am") -> url.replace("series-am", "series-am2")
url.contains("series-nz") -> url.replace("series-nz", "series-nz2")
else -> url
}
}
// =========================== Anime Details ===========================
override fun animeDetailsParse(document: Document): SAnime {
return SAnime.create().apply {
title = document.select("span.post-info:contains(Título) + br").first()?.previousSibling()?.toString()?.trim() ?: ""
val infoMap = document.select("span.post-info").associate { span ->
val label = span.text().trim()
val value = span.nextSibling()?.toString()?.trim() ?: ""
label to value
}
description = document.select("div.su-spoiler-content").first()?.text()?.trim() ?: ""
genre = infoMap["Géneros"]?.substringBefore(".")
author = infoMap["Estudio"]
status = when (infoMap["Episodios"]?.substringAfter("de ")?.trim()) {
infoMap["Episodios"]?.substringBefore(" de")?.trim() -> SAnime.COMPLETED
else -> SAnime.ONGOING
}
// Información adicional para la descripción
val additionalInfo = buildString {
appendLine("\n\nInformación:")
if (!infoMap["Año"].isNullOrBlank()) appendLine("• Año: ${infoMap["Año"]}")
if (!infoMap["Episodios"].isNullOrBlank()) appendLine("• Episodios: ${infoMap["Episodios"]}")
if (!infoMap["Duración"].isNullOrBlank()) appendLine("• Duración: ${infoMap["Duración"]}")
if (!infoMap["Fansub"].isNullOrBlank()) appendLine("• Fansub: ${infoMap["Fansub"]}")
if (!infoMap["Versión"].isNullOrBlank()) appendLine("• Versión: ${infoMap["Versión"]}")
if (!infoMap["Resolución"].isNullOrBlank()) appendLine("• Resolución: ${infoMap["Resolución"]}")
if (!infoMap["Formato"].isNullOrBlank()) appendLine("• Formato: ${infoMap["Formato"]}")
}
description += additionalInfo
}
}
// =============================== Search ===============================
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
val urlElement = element.selectFirst("h2.wp-block-post-title a")
anime.setUrlWithoutDomain(urlElement?.attr("href") ?: "")
anime.title = urlElement?.text() ?: "Título desconocido"
anime.thumbnail_url = element.selectFirst("figure.gs-hover-scale-img img")?.attr("src") ?: ""
Log.d("Azanimex", "URL de la imagen: ${anime.thumbnail_url}")
val genres = element.select("div.taxonomy-tipo a, div.taxonomy-version a").joinToString { it.text() }
anime.genre = genres.ifEmpty { "Desconocido" }
return anime
}
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
override fun searchAnimeSelector(): String = popularAnimeSelector()
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val url = if (page > 1) {
"$baseUrl/page/$page/?s=$query"
} else {
"$baseUrl/?s=$query"
}
return GET(url, headers)
}
// ============================ Video URLS =============================
override fun videoListSelector() = throw Exception("Not used")
override fun videoFromElement(element: Element) = throw Exception("Not used")
override fun videoUrlParse(document: Document): String {
throw Exception("Not used")
}
override suspend fun getVideoList(episode: SEpisode): List<Video> {
val videoUrl = episode.url
val video = Video(videoUrl, "az-animex", videoUrl)
return listOf(video)
}
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request {
throw UnsupportedOperationException()
}
override fun latestUpdatesSelector(): String {
throw UnsupportedOperationException()
}
override fun latestUpdatesFromElement(element: Element): SAnime {
throw UnsupportedOperationException()
}
override fun latestUpdatesNextPageSelector(): String? {
throw UnsupportedOperationException()
}
companion object {
const val PREFIX_SEARCH = "id:"
}
}

View file

@ -0,0 +1,41 @@
package eu.kanade.tachiyomi.animeextension.es.azanimex
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
/**
* Springboard that accepts https://www.az-animex.com/anime/<item> intents
* and redirects them to the main Aniyomi process.
*/
class AzanimexUrlActivity : Activity() {
private val tag = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size > 1) {
val item = pathSegments[1]
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.ANIMESEARCH"
putExtra("query", "${Azanimex.PREFIX_SEARCH}$item")
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e(tag, e.toString())
}
} else {
Log.e(tag, "could not parse uri from intent $intent")
}
finish()
exitProcess(0)
}
}

View file

@ -0,0 +1,38 @@
package eu.kanade.tachiyomi.animeextension.es.azanimex
import kotlinx.serialization.Serializable
@Serializable
data class OneDriveResponse(
val file: FileItem? = null, // El archivo puede ser null
val folder: Folder? = null, // La carpeta también puede ser null
)
@Serializable
data class Folder(
val value: List<FileItem>,
)
@Serializable
data class FileInfo(
val mimeType: String,
)
@Serializable
data class FileItem(
val id: String,
val name: String,
val file: FileInfo,
val video: VideoInfo?,
)
@Serializable
data class VideoInfo(
val height: Int,
val width: Int,
val audioChannels: Int,
val audioBitsPerSample: Int,
val frameRate: Double,
val bitRate: Int,
val duration: Int,
)

View file

@ -1,7 +1,7 @@
ext {
extName = 'Cine24h'
extClass = '.Cine24h'
extVersionCode = 4
extVersionCode = 10
}
apply from: "$rootDir/common.gradle"
@ -12,4 +12,5 @@ dependencies {
implementation(project(':lib:filemoon-extractor'))
implementation(project(':lib:voe-extractor'))
implementation(project(':lib:vidguard-extractor'))
implementation(project(':lib:universal-extractor'))
}

View file

@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.vidguardextractor.VidGuardExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
@ -155,6 +156,7 @@ open class Cine24h : ConfigurableAnimeSource, AnimeHttpSource() {
private val doodExtractor by lazy { DoodExtractor(client) }
private val voeExtractor by lazy { VoeExtractor(client) }
private val vidGuardExtractor by lazy { VidGuardExtractor(client) }
private val universalExtractor by lazy { UniversalExtractor(client) }
private fun serverVideoResolver(url: String): List<Video> {
val embedUrl = url.lowercase()
@ -167,7 +169,7 @@ open class Cine24h : ConfigurableAnimeSource, AnimeHttpSource() {
arrayOf("voe").any(url) -> voeExtractor.videosFromUrl(url)
arrayOf("doodstream", "dood.", "ds2play", "doods.").any(url) -> doodExtractor.videosFromUrl(url)
arrayOf("vembed", "guard", "listeamed", "bembed", "vgfplay").any(url) -> vidGuardExtractor.videosFromUrl(url)
else -> emptyList()
else -> universalExtractor.videosFromUrl(url, headers)
}
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'CineCalidad'
extClass = '.CineCalidad'
extVersionCode = 5
extVersionCode = 16
}
apply from: "$rootDir/common.gradle"
@ -23,4 +23,6 @@ dependencies {
implementation(project(':lib:fastream-extractor'))
implementation(project(':lib:upstream-extractor'))
implementation(project(':lib:streamhidevid-extractor'))
implementation(project(':lib:goodstream-extractor'))
implementation(project(':lib:universal-extractor'))
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View file

@ -15,12 +15,14 @@ import eu.kanade.tachiyomi.lib.burstcloudextractor.BurstCloudExtractor
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.goodstramextractor.GoodStreamExtractor
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
@ -167,7 +169,7 @@ class CineCalidad : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
StreamWishExtractor(client, docHeaders).videosFromUrl(url, videoNameGen = { "StreamWish:$it" })
}
embedUrl.contains("doodstream") || embedUrl.contains("dood.") || embedUrl.contains("ds2play") || embedUrl.contains("doods.") -> {
DoodExtractor(client).videosFromUrl(url.replace("https://doodstream.com/e/", "https://dood.to/e/"), "DoodStream", false)
DoodExtractor(client).videosFromUrl(url.replace("https://doodstream.com/e/", "https://dood.to/e/"), "DoodStream")
}
embedUrl.contains("streamlare") -> StreamlareExtractor(client).videosFromUrl(url)
embedUrl.contains("yourupload") || embedUrl.contains("upload") -> YourUploadExtractor(client).videoFromUrl(url, headers = headers)
@ -175,8 +177,9 @@ class CineCalidad : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
embedUrl.contains("fastream") -> FastreamExtractor(client, headers).videosFromUrl(url, prefix = "Fastream:")
embedUrl.contains("upstream") -> UpstreamExtractor(client).videosFromUrl(url)
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> listOf(StreamTapeExtractor(client).videoFromUrl(url, quality = "StreamTape")!!)
embedUrl.contains("ahvsh") || embedUrl.contains("streamhide") || embedUrl.contains("guccihide") || embedUrl.contains("streamvid") || embedUrl.contains("vidhide") -> StreamHideVidExtractor(client).videosFromUrl(url)
else -> emptyList()
embedUrl.contains("ahvsh") || embedUrl.contains("streamhide") || embedUrl.contains("guccihide") || embedUrl.contains("streamvid") || embedUrl.contains("vidhide") -> StreamHideVidExtractor(client, headers).videosFromUrl(url)
embedUrl.contains("goodstream") -> GoodStreamExtractor(client, headers).videosFromUrl(url, name = "GoodStream: ")
else -> UniversalExtractor(client).videosFromUrl(url, headers)
}
}.getOrNull() ?: emptyList()
}

View file

@ -3,7 +3,7 @@ ext {
extClass = '.Cineplus123'
themePkg = 'dooplay'
baseUrl = 'https://cineplus123.org'
overrideVersionCode = 3
overrideVersionCode = 5
}
apply from: "$rootDir/common.gradle"
@ -11,4 +11,5 @@ apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:streamwish-extractor"))
implementation(project(":lib:uqload-extractor"))
implementation(project(":lib:universal-extractor"))
}

View file

@ -5,6 +5,7 @@ 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.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
import eu.kanade.tachiyomi.network.GET
@ -39,6 +40,7 @@ class Cineplus123 : DooPlay(
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
private val universalExtractor by lazy { UniversalExtractor(client) }
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
@ -56,8 +58,8 @@ class Cineplus123 : DooPlay(
return when {
"uqload" in url -> uqloadExtractor.videosFromUrl(url, "$lang -")
"strwish" in url -> streamWishExtractor.videosFromUrl(url, lang)
else -> null
} ?: emptyList()
else -> universalExtractor.videosFromUrl(url, headers, prefix = lang)
}
}
private fun getPlayerUrl(player: Element): String? {

View file

@ -1,7 +1,7 @@
ext {
extName = 'Cuevana'
extClass = '.CuevanaFactory'
extVersionCode = 36
extVersionCode = 45
}
apply from: "$rootDir/common.gradle"
@ -22,5 +22,6 @@ dependencies {
implementation(project(':lib:burstcloud-extractor'))
implementation(project(':lib:fastream-extractor'))
implementation(project(':lib:upstream-extractor'))
implementation(project(':lib:universal-extractor'))
implementation(libs.jsunpacker)
}

View file

@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
@ -160,98 +161,121 @@ class CuevanaCh(override val name: String, override val baseUrl: String) : Confi
val videoList = mutableListOf<Video>()
val embedUrl = url.lowercase()
try {
if (embedUrl.contains("voe")) {
VoeExtractor(client).videosFromUrl(url, prefix).also(videoList::addAll)
}
if ((embedUrl.contains("amazon") || embedUrl.contains("amz")) && !embedUrl.contains("disable")) {
val body = client.newCall(GET(url)).execute().asJsoup()
if (body.select("script:containsData(var shareId)").toString().isNotBlank()) {
val shareId = body.selectFirst("script:containsData(var shareId)")!!.data()
.substringAfter("shareId = \"").substringBefore("\"")
val amazonApiJson = client.newCall(GET("https://www.amazon.com/drive/v1/shares/$shareId?resourceVersion=V2&ContentType=JSON&asset=ALL"))
.execute().asJsoup()
val epId = amazonApiJson.toString().substringAfter("\"id\":\"").substringBefore("\"")
val amazonApi =
client.newCall(GET("https://www.amazon.com/drive/v1/nodes/$epId/children?resourceVersion=V2&ContentType=JSON&limit=200&sort=%5B%22kind+DESC%22%2C+%22modifiedDate+DESC%22%5D&asset=ALL&tempLink=true&shareId=$shareId"))
return when {
embedUrl.contains("voe") -> {
VoeExtractor(client).videosFromUrl(url, prefix).also(videoList::addAll)
videoList
}
(embedUrl.contains("amazon") || embedUrl.contains("amz")) && !embedUrl.contains("disable") -> {
val body = client.newCall(GET(url)).execute().asJsoup()
if (body.select("script:containsData(var shareId)").toString().isNotBlank()) {
val shareId = body.selectFirst("script:containsData(var shareId)")!!.data()
.substringAfter("shareId = \"").substringBefore("\"")
val amazonApiJson = client.newCall(GET("https://www.amazon.com/drive/v1/shares/$shareId?resourceVersion=V2&ContentType=JSON&asset=ALL"))
.execute().asJsoup()
val videoUrl = amazonApi.toString().substringAfter("\"FOLDER\":").substringAfter("tempLink\":\"").substringBefore("\"")
videoList.add(Video(videoUrl, "$prefix Amazon", videoUrl))
val epId = amazonApiJson.toString().substringAfter("\"id\":\"").substringBefore("\"")
val amazonApi =
client.newCall(GET("https://www.amazon.com/drive/v1/nodes/$epId/children?resourceVersion=V2&ContentType=JSON&limit=200&sort=%5B%22kind+DESC%22%2C+%22modifiedDate+DESC%22%5D&asset=ALL&tempLink=true&shareId=$shareId"))
.execute().asJsoup()
val videoUrl = amazonApi.toString().substringAfter("\"FOLDER\":").substringAfter("tempLink\":\"").substringBefore("\"")
videoList.add(Video(videoUrl, "$prefix Amazon", videoUrl))
}
videoList
}
}
if (embedUrl.contains("ok.ru") || embedUrl.contains("okru")) {
OkruExtractor(client).videosFromUrl(url, prefix).also(videoList::addAll)
}
if (embedUrl.contains("filemoon") || embedUrl.contains("moonplayer")) {
val vidHeaders = headers.newBuilder()
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
FilemoonExtractor(client).videosFromUrl(url, prefix = "$prefix Filemoon:", headers = vidHeaders).also(videoList::addAll)
}
if (embedUrl.contains("uqload")) {
UqloadExtractor(client).videosFromUrl(url, prefix = prefix).also(videoList::addAll)
}
if (embedUrl.contains("mp4upload")) {
Mp4uploadExtractor(client).videosFromUrl(url, headers, prefix = prefix).let { videoList.addAll(it) }
}
if (embedUrl.contains("wishembed") || embedUrl.contains("streamwish") || embedUrl.contains("strwish") || embedUrl.contains("wish")) {
val docHeaders = headers.newBuilder()
.add("Origin", "https://streamwish.to")
.add("Referer", "https://streamwish.to/")
.build()
StreamWishExtractor(client, docHeaders).videosFromUrl(url, videoNameGen = { "$prefix StreamWish:$it" }).also(videoList::addAll)
}
if (embedUrl.contains("doodstream") || embedUrl.contains("dood.")) {
val url2 = url.replace("https://doodstream.com/e/", "https://dood.to/e/")
DoodExtractor(client).videoFromUrl(url2, "$prefix DoodStream", false)?.let { videoList.add(it) }
}
if (embedUrl.contains("streamlare")) {
StreamlareExtractor(client).videosFromUrl(url, prefix = prefix).let { videoList.addAll(it) }
}
if (embedUrl.contains("yourupload") || embedUrl.contains("upload")) {
YourUploadExtractor(client).videoFromUrl(url, headers = headers, prefix = prefix).let { videoList.addAll(it) }
}
if (embedUrl.contains("burstcloud") || embedUrl.contains("burst")) {
BurstCloudExtractor(client).videoFromUrl(url, headers = headers, prefix = prefix).let { videoList.addAll(it) }
}
if (embedUrl.contains("fastream")) {
FastreamExtractor(client, headers).videosFromUrl(url, prefix = "$prefix Fastream:").also(videoList::addAll)
}
if (embedUrl.contains("upstream")) {
UpstreamExtractor(client).videosFromUrl(url, prefix = prefix).let { videoList.addAll(it) }
}
if (embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape")) {
StreamTapeExtractor(client).videoFromUrl(url, quality = "$prefix StreamTape")?.let { videoList.add(it) }
}
if (embedUrl.contains("tomatomatela")) {
runCatching {
val mainUrl = url.substringBefore("/embed.html#").substringAfter("https://")
val headers = headers.newBuilder()
.set("authority", mainUrl)
.set("accept", "application/json, text/javascript, */*; q=0.01")
.set("accept-language", "es-MX,es-419;q=0.9,es;q=0.8,en;q=0.7")
.set("sec-ch-ua", "\"Chromium\";v=\"106\", \"Google Chrome\";v=\"106\", \"Not;A=Brand\";v=\"99\"")
.set("sec-ch-ua-mobile", "?0")
.set("sec-ch-ua-platform", "Windows")
.set("sec-fetch-dest", "empty")
.set("sec-fetch-mode", "cors")
.set("sec-fetch-site", "same-origin")
.set("x-requested-with", "XMLHttpRequest")
embedUrl.contains("ok.ru") || embedUrl.contains("okru") -> {
OkruExtractor(client).videosFromUrl(url, prefix).also(videoList::addAll)
videoList
}
embedUrl.contains("filemoon") || embedUrl.contains("moonplayer") -> {
val vidHeaders = headers.newBuilder()
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
val token = url.substringAfter("/embed.html#")
val urlRequest = "https://$mainUrl/details.php?v=$token"
val response = client.newCall(GET(urlRequest, headers = headers)).execute().asJsoup()
val bodyText = response.select("body").text()
val json = json.decodeFromString<JsonObject>(bodyText)
val status = json["status"]!!.jsonPrimitive!!.content
val file = json["file"]!!.jsonPrimitive!!.content
if (status == "200") { videoList.add(Video(file, "$prefix Tomatomatela", file, headers = null)) }
FilemoonExtractor(client).videosFromUrl(url, prefix = "$prefix Filemoon:", headers = vidHeaders).also(videoList::addAll)
videoList
}
embedUrl.contains("uqload") -> {
UqloadExtractor(client).videosFromUrl(url, prefix = prefix).also(videoList::addAll)
videoList
}
embedUrl.contains("mp4upload") -> {
Mp4uploadExtractor(client).videosFromUrl(url, headers, prefix = prefix).let { videoList.addAll(it) }
videoList
}
embedUrl.contains("wishembed") || embedUrl.contains("streamwish") || embedUrl.contains("strwish") || embedUrl.contains("wish") -> {
val docHeaders = headers.newBuilder()
.add("Origin", "https://streamwish.to")
.add("Referer", "https://streamwish.to/")
.build()
StreamWishExtractor(client, docHeaders).videosFromUrl(url, videoNameGen = { "$prefix StreamWish:$it" }).also(videoList::addAll)
videoList
}
embedUrl.contains("doodstream") || embedUrl.contains("dood.") -> {
val url2 = url.replace("https://doodstream.com/e/", "https://dood.to/e/")
DoodExtractor(client).videoFromUrl(url2, "$prefix DoodStream")?.let { videoList.add(it) }
videoList
}
embedUrl.contains("streamlare") -> {
StreamlareExtractor(client).videosFromUrl(url, prefix = prefix).let { videoList.addAll(it) }
videoList
}
embedUrl.contains("yourupload") || embedUrl.contains("upload") -> {
YourUploadExtractor(client).videoFromUrl(url, headers = headers, prefix = prefix).let { videoList.addAll(it) }
videoList
}
embedUrl.contains("burstcloud") || embedUrl.contains("burst") -> {
BurstCloudExtractor(client).videoFromUrl(url, headers = headers, prefix = prefix).let { videoList.addAll(it) }
videoList
}
embedUrl.contains("fastream") -> {
FastreamExtractor(client, headers).videosFromUrl(url, prefix = "$prefix Fastream:").also(videoList::addAll)
videoList
}
embedUrl.contains("upstream") -> {
UpstreamExtractor(client).videosFromUrl(url, prefix = prefix).let { videoList.addAll(it) }
videoList
}
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> {
StreamTapeExtractor(client).videoFromUrl(url, quality = "$prefix StreamTape")?.let { videoList.add(it) }
videoList
}
embedUrl.contains("tomatomatela") -> {
runCatching {
val mainUrl = url.substringBefore("/embed.html#").substringAfter("https://")
val headers = headers.newBuilder()
.set("authority", mainUrl)
.set("accept", "application/json, text/javascript, */*; q=0.01")
.set("accept-language", "es-MX,es-419;q=0.9,es;q=0.8,en;q=0.7")
.set("sec-ch-ua", "\"Chromium\";v=\"106\", \"Google Chrome\";v=\"106\", \"Not;A=Brand\";v=\"99\"")
.set("sec-ch-ua-mobile", "?0")
.set("sec-ch-ua-platform", "Windows")
.set("sec-fetch-dest", "empty")
.set("sec-fetch-mode", "cors")
.set("sec-fetch-site", "same-origin")
.set("x-requested-with", "XMLHttpRequest")
.build()
val token = url.substringAfter("/embed.html#")
val urlRequest = "https://$mainUrl/details.php?v=$token"
val response = client.newCall(GET(urlRequest, headers = headers)).execute().asJsoup()
val bodyText = response.select("body").text()
val json = json.decodeFromString<JsonObject>(bodyText)
val status = json["status"]!!.jsonPrimitive.content
val file = json["file"]!!.jsonPrimitive.content
if (status == "200") { videoList.add(Video(file, "$prefix Tomatomatela", file, headers = null)) }
}
videoList
}
embedUrl.contains("filelions") || embedUrl.contains("lion") -> {
StreamWishExtractor(client, headers).videosFromUrl(url, videoNameGen = { "$prefix FileLions:$it" }).also(videoList::addAll)
videoList
}
else -> {
UniversalExtractor(client).videosFromUrl(url, headers, prefix = prefix).also(videoList::addAll)
}
}
if (embedUrl.contains("filelions") || embedUrl.contains("lion")) {
StreamWishExtractor(client, headers).videosFromUrl(url, videoNameGen = { "$prefix FileLions:$it" }).also(videoList::addAll)
}
} catch (_: Exception) { }
} catch (e: Exception) {
e.printStackTrace()
}
return videoList
}

View file

@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.network.GET
@ -180,34 +181,47 @@ class CuevanaEu(override val name: String, override val baseUrl: String) : Confi
private fun loadExtractor(url: String, prefix: String = ""): List<Video> {
val videoList = mutableListOf<Video>()
val embedUrl = url.lowercase()
if (embedUrl.contains("yourupload")) {
val videos = YourUploadExtractor(client).videoFromUrl(url, headers = headers)
videoList.addAll(videos)
return when {
embedUrl.contains("yourupload") -> {
val videos = YourUploadExtractor(client).videoFromUrl(url, headers = headers)
videoList.addAll(videos)
videoList
}
embedUrl.contains("doodstream") || embedUrl.contains("dood.") -> {
DoodExtractor(client).videoFromUrl(url, "$prefix DoodStream")
?.let { videoList.add(it) }
videoList
}
embedUrl.contains("okru") || embedUrl.contains("ok.ru") -> {
OkruExtractor(client).videosFromUrl(url, prefix, true).also(videoList::addAll)
videoList
}
embedUrl.contains("voe") -> {
VoeExtractor(client).videosFromUrl(url, prefix).also(videoList::addAll)
videoList
}
embedUrl.contains("streamtape") -> {
StreamTapeExtractor(client).videoFromUrl(url, "$prefix StreamTape")?.let { videoList.add(it) }
videoList
}
embedUrl.contains("wishembed") || embedUrl.contains("streamwish") || embedUrl.contains("wish") -> {
StreamWishExtractor(client, headers).videosFromUrl(url) { "$prefix StreamWish:$it" }
.also(videoList::addAll)
videoList
}
embedUrl.contains("filemoon") || embedUrl.contains("moonplayer") -> {
FilemoonExtractor(client).videosFromUrl(url, "$prefix Filemoon:").also(videoList::addAll)
videoList
}
embedUrl.contains("filelions") || embedUrl.contains("lion") -> {
StreamWishExtractor(client, headers).videosFromUrl(url, videoNameGen = { "$prefix FileLions:$it" }).also(videoList::addAll)
videoList
}
else -> {
UniversalExtractor(client).videosFromUrl(url, headers, prefix = prefix).also(videoList::addAll)
videoList
}
}
if (embedUrl.contains("doodstream") || embedUrl.contains("dood.")) {
DoodExtractor(client).videoFromUrl(url, "$prefix DoodStream", false)
?.let { videoList.add(it) }
}
if (embedUrl.contains("okru") || embedUrl.contains("ok.ru")) {
OkruExtractor(client).videosFromUrl(url, prefix, true).also(videoList::addAll)
}
if (embedUrl.contains("voe")) {
VoeExtractor(client).videosFromUrl(url, prefix).also(videoList::addAll)
}
if (embedUrl.contains("streamtape")) {
StreamTapeExtractor(client).videoFromUrl(url, "$prefix StreamTape")?.let { videoList.add(it) }
}
if (embedUrl.contains("wishembed") || embedUrl.contains("streamwish") || embedUrl.contains("wish")) {
StreamWishExtractor(client, headers).videosFromUrl(url) { "$prefix StreamWish:$it" }
.also(videoList::addAll)
}
if (embedUrl.contains("filemoon") || embedUrl.contains("moonplayer")) {
FilemoonExtractor(client).videosFromUrl(url, "$prefix Filemoon:").also(videoList::addAll)
}
if (embedUrl.contains("filelions") || embedUrl.contains("lion")) {
StreamWishExtractor(client, headers).videosFromUrl(url, videoNameGen = { "$prefix FileLions:$it" }).also(videoList::addAll)
}
return videoList
}
override fun videoListSelector() = throw UnsupportedOperationException()

View file

@ -0,0 +1,18 @@
ext {
extName = 'DeTodoPeliculas'
extClass = '.DeTodoPeliculas'
themePkg = 'dooplay'
baseUrl = 'https://detodopeliculas.nu'
overrideVersionCode = 2
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:streamwish-extractor"))
implementation(project(":lib:uqload-extractor"))
implementation(project(":lib:universal-extractor"))
implementation(project(':lib:streamhidevid-extractor'))
implementation(project(':lib:vidguard-extractor'))
implementation(project(':lib:voe-extractor'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View file

@ -0,0 +1,205 @@
package eu.kanade.tachiyomi.animeextension.es.detodopeliculas
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.streamhidevidextractor.StreamHideVidExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.vidguardextractor.VidGuardExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
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 DeTodoPeliculas : DooPlay(
"es",
"DeTodo Peliculas",
"https://detodopeliculas.nu",
) {
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/novedades/page/$page")
override fun popularAnimeSelector() = latestUpdatesSelector()
override fun popularAnimeNextPageSelector() = latestUpdatesNextPageSelector()
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/peliculas-de-estreno/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) }
private val universalExtractor by lazy { UniversalExtractor(client) }
private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client, headers) }
private val vidGuardExtractor by lazy { VidGuardExtractor(client) }
private val voeExtractor by lazy { VoeExtractor(client) }
// ============================ 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 flagSrc = player.selectFirst("span.flag img")?.attr("data-lazy-src") ?: ""
val lang = when {
"sub.png" in flagSrc -> "[SUB]"
"cas.png" in flagSrc -> "[CAST]"
"lat.png" in flagSrc -> "[LAT]"
else -> "UNKNOWN"
}
val url = getPlayerUrl(player)
?: return@parallelFlatMapBlocking emptyList<Video>()
extractVideos(url, lang)
}
}
private fun extractVideos(url: String, lang: String): List<Video> {
val vidHideDomains = listOf("vidhide", "VidHidePro", "luluvdo", "vidhideplus")
try {
val videos = vidHideDomains.firstOrNull { it in url }?.let { domain ->
streamHideVidExtractor.videosFromUrl(url, videoNameGen = { "$lang - $domain : $it" })
} ?: emptyList()
return when {
videos.isNotEmpty() -> videos
"uqload" in url -> uqloadExtractor.videosFromUrl(url, "$lang - ")
"strwish" in url -> streamWishExtractor.videosFromUrl(url, "$lang - ")
"vidguard" in url || "listeamed" in url -> vidGuardExtractor.videosFromUrl(url, "$lang - ")
"voe" in url -> voeExtractor.videosFromUrl(url, "$lang - ")
else -> emptyList()
}
} catch (e: Exception) {
e.printStackTrace()
return 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() = DeTodoPeliculasFilters.FILTER_LIST
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = DeTodoPeliculasFilters.getSearchParameters(filters)
val path = when {
params.genre.isNotBlank() -> {
if (params.genre in listOf("peliculas-de-estreno", "novedades", "peliculas-recomendadas", "peliculas")) {
"/${params.genre}"
} else {
"/genero/${params.genre}"
}
}
else -> buildString {
append(
when {
query.isNotBlank() -> "/?s=$query"
else -> "/"
},
)
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 = "[LAT]"
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "Uqload"
private val PREF_LANG_ENTRIES = arrayOf("[LAT]", "[SUB]", "[CAST]")
private val PREF_LANG_VALUES = arrayOf("[LAT]", "[SUB]", "[CAST]")
private val SERVER_LIST = arrayOf("StreamWish", "Uqload", "VidGuard", "StreamHideVid", "Voe")
}
}

View file

@ -0,0 +1,74 @@
package eu.kanade.tachiyomi.animeextension.es.detodopeliculas
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
object DeTodoPeliculasFilters {
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 GenreFilter : UriPartFilter("Generos", AnimesOnlineNinjaData.GENRES)
class YearFilter : UriPartFilter("Año", AnimesOnlineNinjaData.YEARS)
val FILTER_LIST get() = AnimeFilterList(
GenreFilter(),
YearFilter(),
)
data class FilterSearchParams(
val isInverted: Boolean = false,
val genre: String = "",
val year: String = "",
)
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
if (filters.isEmpty()) return FilterSearchParams()
return FilterSearchParams(
genre = filters.asUriPart<GenreFilter>(),
year = filters.asUriPart<YearFilter>(),
)
}
private object AnimesOnlineNinjaData {
val EVERY = Pair("Seleccionar", "")
val GENRES = arrayOf(
EVERY,
Pair("Acción", "accion"),
Pair("Animación", "animacion"),
Pair("Aventura", "aventura"),
Pair("Bélica", "belica"),
Pair("Ciencia ficción", "ciencia-ficcion"),
Pair("Comedia", "comedia"),
Pair("Crimen", "crimen"),
Pair("Documental", "documental"),
Pair("Drama", "drama"),
Pair("Familia", "familia"),
)
val YEARS = arrayOf(EVERY) + (2024 downTo 1979).map {
Pair(it.toString(), it.toString())
}.toTypedArray()
}
}

View file

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

View file

@ -498,7 +498,7 @@ class Doramasflix : ConfigurableAnimeSource, AnimeHttpSource() {
"uqload" in embedUrl -> UqloadExtractor(client).videosFromUrl(url, prefix = prefix)
"mp4upload" in embedUrl -> Mp4uploadExtractor(client).videosFromUrl(url, prefix = "$prefix ", headers = headers)
"doodstream" in embedUrl || "dood." in embedUrl ->
listOf(DoodExtractor(client).videoFromUrl(url.replace("https://doodstream.com/e/", "https://dood.to/e/"), "$prefix DoodStream", false)!!)
listOf(DoodExtractor(client).videoFromUrl(url.replace("https://doodstream.com/e/", "https://dood.to/e/"), "$prefix DoodStream")!!)
"streamlare" in embedUrl -> StreamlareExtractor(client).videosFromUrl(url, prefix = prefix)
"yourupload" in embedUrl || "upload" in embedUrl -> YourUploadExtractor(client).videoFromUrl(url, headers = headers, prefix = "$prefix ")
"wishembed" in embedUrl || "streamwish" in embedUrl || "strwish" in embedUrl || "wish" in embedUrl -> {
@ -512,7 +512,7 @@ class Doramasflix : ConfigurableAnimeSource, AnimeHttpSource() {
"fastream" in embedUrl -> FastreamExtractor(client, headers).videosFromUrl(url, prefix = "$prefix Fastream:")
"upstream" in embedUrl -> UpstreamExtractor(client).videosFromUrl(url, prefix = "$prefix ")
"streamtape" in embedUrl || "stp" in embedUrl || "stape" in embedUrl -> listOf(StreamTapeExtractor(client).videoFromUrl(url, quality = "$prefix StreamTape")!!)
"ahvsh" in embedUrl || "streamhide" in embedUrl -> StreamHideVidExtractor(client).videosFromUrl(url, "$prefix ")
"ahvsh" in embedUrl || "streamhide" in embedUrl -> StreamHideVidExtractor(client, headers).videosFromUrl(url, videoNameGen = { "$prefix StreamHide:$it" })
"filelions" in embedUrl || "lion" in embedUrl -> StreamWishExtractor(client, headers).videosFromUrl(url, videoNameGen = { "$prefix FileLions:$it" })
"vudeo" in embedUrl || "vudea" in embedUrl -> VudeoExtractor(client).videosFromUrl(url, "$prefix ")
else -> emptyList()

View file

@ -1,7 +1,7 @@
ext {
extName = 'Doramasyt'
extClass = '.Doramasyt'
extVersionCode = 17
extVersionCode = 19
}
apply from: "$rootDir/common.gradle"
@ -15,4 +15,5 @@ dependencies {
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:mixdrop-extractor'))
implementation(project(':lib:universal-extractor'))
}

View file

@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.universalextractor.UniversalExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
@ -190,6 +191,7 @@ class Doramasyt : ConfigurableAnimeSource, AnimeHttpSource() {
private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val universalExtractor by lazy { UniversalExtractor(client) }
private fun serverVideoResolver(url: String): List<Video> {
val embedUrl = url.lowercase()
@ -200,10 +202,10 @@ class Doramasyt : ConfigurableAnimeSource, AnimeHttpSource() {
embedUrl.contains("filemoon") || embedUrl.contains("moonplayer") -> filemoonExtractor.videosFromUrl(url, prefix = "Filemoon:")
embedUrl.contains("wishembed") || embedUrl.contains("streamwish") || embedUrl.contains("strwish") || embedUrl.contains("wish") || embedUrl.contains("wishfast") -> streamwishExtractor.videosFromUrl(url, videoNameGen = { "StreamWish:$it" })
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> streamTapeExtractor.videosFromUrl(url)
embedUrl.contains("doodstream") || embedUrl.contains("dood.") || embedUrl.contains("ds2play") || embedUrl.contains("doods.") -> doodExtractor.videosFromUrl(url, "DoodStream", false)
embedUrl.contains("doodstream") || embedUrl.contains("dood.") || embedUrl.contains("ds2play") || embedUrl.contains("doods.") -> doodExtractor.videosFromUrl(url)
embedUrl.contains("filelions") || embedUrl.contains("lion") -> streamwishExtractor.videosFromUrl(url, videoNameGen = { "FileLions:$it" })
embedUrl.contains("mix") -> mixdropExtractor.videosFromUrl(url)
else -> emptyList()
else -> universalExtractor.videosFromUrl(url, headers)
}
}

View file

@ -0,0 +1,27 @@
package eu.kanade.tachiyomi.animeextension.es.doramasyt.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class SolidFilesExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val videoList = mutableListOf<Video>()
return try {
val document = client.newCall(GET(url)).execute().asJsoup()
document.select("script").forEach { script ->
if (script.data().contains("\"downloadUrl\":")) {
val data = script.data().substringAfter("\"downloadUrl\":").substringBefore(",")
val url = data.replace("\"", "")
val videoUrl = url
val quality = prefix + "SolidFiles"
videoList.add(Video(videoUrl, quality, videoUrl))
}
}
videoList
} catch (e: Exception) {
videoList
}
}
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'EnNovelas'
extClass = '.EnNovelas'
extVersionCode = 12
extVersionCode = 19
}
apply from: "$rootDir/common.gradle"
@ -14,4 +14,5 @@ dependencies {
implementation(project(':lib:streamlare-extractor'))
implementation(project(':lib:uqload-extractor'))
implementation(project(':lib:vudeo-extractor'))
implementation(project(':lib:dailymotion-extractor'))
}

View file

@ -5,12 +5,14 @@ import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.dailymotionextractor.DailymotionExtractor
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
@ -20,6 +22,7 @@ import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType
@ -38,7 +41,7 @@ class EnNovelas : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "EnNovelas"
override val baseUrl = "https://u.ennovelas.net"
override val baseUrl = "https://tv.ennovelas.net/"
override val lang = "es"
@ -50,7 +53,7 @@ class EnNovelas : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun popularAnimeSelector(): String = ".block-post"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/telenovelas/page/$page")
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/category/novelas-completas/page/$page")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
@ -146,7 +149,7 @@ class EnNovelas : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
.add("upgrade-insecure-requests", "1")
.build()
client.newCall(POST(urlRequest, headers, body)).execute().asJsoup().select(".serversList li").map {
client.newCall(POST(urlRequest, headers, body)).execute().asJsoup().select(".serversList li").map { it ->
val frameString = it.attr("abs:data-server")
val link = frameString.substringAfter("src='").substringBefore("'")
.replace("https://api.mycdn.moe/sblink.php?id=", "https://streamsb.net/e/")
@ -188,7 +191,12 @@ class EnNovelas : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
if (link.contains("streamlare")) {
try {
StreamlareExtractor(client).videosFromUrl(link)?.let { videoList.addAll(it) }
StreamlareExtractor(client).videosFromUrl(link).let { videoList.addAll(it) }
} catch (_: Exception) {}
}
if (link.contains("dailymotion")) {
try {
DailymotionExtractor(client, headers).videosFromUrl(link).let { videoList.addAll(it) }
} catch (_: Exception) {}
}
}
@ -219,13 +227,6 @@ class EnNovelas : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return this
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
return when {
query.isNotBlank() -> GET("$baseUrl/search/$query/page/$page/")
else -> popularAnimeRequest(page)
}
}
override fun searchAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val animeList = mutableListOf<SAnime>()
@ -242,9 +243,48 @@ class EnNovelas : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
return AnimesPage(animeList, hasNextPage)
}
override fun searchAnimeFromElement(element: Element): SAnime = throw UnsupportedOperationException()
override fun searchAnimeNextPageSelector(): String = throw UnsupportedOperationException()
override fun searchAnimeSelector(): String = throw UnsupportedOperationException()
// =============================== Search ===============================
override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
val id = query.removePrefix(PREFIX_SEARCH)
client.newCall(GET("$baseUrl/search/$id", headers))
.awaitSuccess()
.use(::searchAnimeByIdParse)
} else {
super.getSearchAnime(page, query, filters)
}
}
private fun searchAnimeByIdParse(response: Response): AnimesPage {
val details = animeDetailsParse(response.asJsoup())
.apply {
setUrlWithoutDomain(response.request.url.toString())
initialized = true
}
return AnimesPage(listOf(details), false)
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
val yearFilter = filterList.find { it is YearFilter } as YearFilter
val typeFilter = filterList.find { it is TypeFilter } as TypeFilter
return when {
query.isNotBlank() -> GET("$baseUrl/search/$query/page/$page/")
genreFilter.state != 0 -> GET("$baseUrl/${genreFilter.toUriPart()}/page/$page/")
yearFilter.state != 0 -> GET("$baseUrl/${yearFilter.toUriPart()}/page/$page/")
typeFilter.state != 0 -> GET("$baseUrl/${typeFilter.toUriPart()}/page/$page/")
else -> popularAnimeRequest(page)
}
}
override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
override fun searchAnimeSelector(): String = popularAnimeSelector()
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
@ -267,6 +307,62 @@ class EnNovelas : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
}
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
AnimeFilter.Header("La búsqueda por texto ignora el filtro"),
GenreFilter(),
YearFilter(),
TypeFilter(),
)
private class GenreFilter : UriPartFilter(
"Categorías",
arrayOf(
Pair("Seleccionar", ""),
Pair("Novelas Mexicanas", "genre/novelas-mexicanas"),
Pair("Novelas Colombianas", "genre/novelas-colombianas"),
Pair("Series Y Novelas Turcas", "genre/series-y-novelas-turcas"),
Pair("Novelas Brasileñas", "genre/novelas-brasilenas"),
Pair("Novelas Americanas", "genre/novelas-americanas"),
Pair("Novelas Españolas", "genre/novelas-espanolas"),
Pair("Novelas Chilenas", "genre/telenovelas-chilenas"),
Pair("Novelas Peruanas", "genre/novelas-peruanas"),
Pair("Novelas Venezolanas", "genre/novelas-venezolanas"),
Pair("Novelas Reino Unido", "genre/novelas-reino-unido"),
Pair("Novelas Argentinas", "genre/novelas-argentinas"),
Pair("Novelas Filipinas", "genre/novelas-filipinas"),
Pair("Novelas Indias", "genre/novelas-indias"),
),
)
private class YearFilter : UriPartFilter(
"Años",
arrayOf(
Pair("Seleccionar", ""),
Pair("2024", "years/2024"),
Pair("2023", "years/2023"),
Pair("2022", "years/2022"),
Pair("2021", "years/2021"),
Pair("2020", "years/2020"),
Pair("2019", "years/2019"),
Pair("2018", "years/2018"),
Pair("2017", "years/2017"),
Pair("2016", "years/2016"),
Pair("2015", "years/2015"),
),
)
private class TypeFilter : UriPartFilter(
"Tipo",
arrayOf(
Pair("Seleccionar", ""),
Pair("Peliculas", "movies"),
),
)
private 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
}
override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector()
override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
@ -275,6 +371,10 @@ class EnNovelas : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun latestUpdatesSelector() = popularAnimeSelector()
companion object {
const val PREFIX_SEARCH = "id:"
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val qualities = arrayOf(
"Okru:1080p", "Okru:720p", "Okru:480p", "Okru:360p", "Okru:240p", "Okru:144p", // Okru

View file

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

View file

@ -135,7 +135,7 @@ class EstrenosDoramas : ConfigurableAnimeSource, AnimeHttpSource() {
/*--------------------------------Video extractors------------------------------------*/
private val okruExtractor by lazy { OkruExtractor(client) }
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client) }
private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client, headers) }
private val voeExtractor by lazy { VoeExtractor(client) }
private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
private val vidGuardExtractor by lazy { VidGuardExtractor(client) }

View file

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

View file

@ -116,7 +116,7 @@ class FanPelis : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
if (embedUrl.contains("doodstream") || embedUrl.contains("dood")) {
val video = try {
DoodExtractor(client).videoFromUrl(url, "DoodStream", true)
DoodExtractor(client).videoFromUrl(url, "DoodStream")
} catch (e: Exception) {
null
}

View file

@ -0,0 +1,33 @@
ext {
extName = 'FlixLatam'
extClass = '.FlixLatam'
themePkg = 'dooplay'
baseUrl = 'https://flixlatam.com'
overrideVersionCode = 3
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:vudeo-extractor'))
implementation(project(':lib:uqload-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:filemoon-extractor'))
implementation(project(':lib:streamlare-extractor'))
implementation(project(':lib:yourupload-extractor'))
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:voe-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:mp4upload-extractor'))
implementation(project(':lib:mixdrop-extractor'))
implementation(project(':lib:burstcloud-extractor'))
implementation(project(':lib:fastream-extractor'))
implementation(project(':lib:upstream-extractor'))
implementation(project(':lib:streamhidevid-extractor'))
implementation(project(':lib:streamsilk-extractor'))
implementation(project(':lib:vidguard-extractor'))
implementation(project(':lib:universal-extractor'))
implementation(project(':lib:cryptoaes'))
implementation(libs.jsunpacker)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Some files were not shown because too many files have changed in this diff Show more