diff --git a/lib/megacloud-extractor/src/main/java/eu/kanade/tachiyomi/lib/megacloudextractor/MegaCloudExtractor.kt b/lib/megacloud-extractor/src/main/java/eu/kanade/tachiyomi/lib/megacloudextractor/MegaCloudExtractor.kt index a9ad8b19..ead4b50b 100644 --- a/lib/megacloud-extractor/src/main/java/eu/kanade/tachiyomi/lib/megacloudextractor/MegaCloudExtractor.kt +++ b/lib/megacloud-extractor/src/main/java/eu/kanade/tachiyomi/lib/megacloudextractor/MegaCloudExtractor.kt @@ -141,7 +141,7 @@ class MegaCloudExtractor( } private fun getVideoDto(url: String): VideoDto { - val type = if (url.startsWith("https://megacloud.tv")) 0 else 1 + val type = if (url.startsWith("https://megacloud.tv") or url.startsWith("https://megacloud.club")) 0 else 1 val keyType = SOURCES_KEY[type] diff --git a/src/en/kaido/build.gradle b/src/en/kaido/build.gradle index 1f239eb6..6bcd6429 100644 --- a/src/en/kaido/build.gradle +++ b/src/en/kaido/build.gradle @@ -3,7 +3,7 @@ ext { extClass = '.Kaido' themePkg = 'zorotheme' baseUrl = 'https://kaido.to' - overrideVersionCode = 7 + overrideVersionCode = 8 } apply from: "$rootDir/common.gradle" \ No newline at end of file diff --git a/src/en/zoro/build.gradle b/src/en/zoro/build.gradle index a6497a04..3266e57a 100644 --- a/src/en/zoro/build.gradle +++ b/src/en/zoro/build.gradle @@ -3,7 +3,7 @@ ext { extClass = '.HiAnime' themePkg = 'zorotheme' baseUrl = 'https://hianime.to' - overrideVersionCode = 44 + overrideVersionCode = 46 } apply from: "$rootDir/common.gradle" \ No newline at end of file diff --git a/src/en/zoro/src/eu/kanade/tachiyomi/animeextension/en/zoro/HiAnime.kt b/src/en/zoro/src/eu/kanade/tachiyomi/animeextension/en/zoro/HiAnime.kt index 7f38b36f..f05e35d3 100644 --- a/src/en/zoro/src/eu/kanade/tachiyomi/animeextension/en/zoro/HiAnime.kt +++ b/src/en/zoro/src/eu/kanade/tachiyomi/animeextension/en/zoro/HiAnime.kt @@ -1,11 +1,13 @@ package eu.kanade.tachiyomi.animeextension.en.zoro +import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.lib.megacloudextractor.MegaCloudExtractor import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor import eu.kanade.tachiyomi.multisrc.zorotheme.ZoroTheme import eu.kanade.tachiyomi.network.GET import okhttp3.Request +import org.jsoup.nodes.Element class HiAnime : ZoroTheme( "en", @@ -26,6 +28,12 @@ class HiAnime : ZoroTheme( override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/recently-updated?page=$page", docHeaders) + override fun popularAnimeFromElement(element: Element): SAnime { + return super.popularAnimeFromElement(element).apply { + url = url.substringBefore("?") + } + } + override fun extractVideo(server: VideoData): List<Video> { return when (server.name) { "StreamTape" -> { diff --git a/src/es/zonaleros/build.gradle b/src/es/zonaleros/build.gradle new file mode 100644 index 00000000..30e67c19 --- /dev/null +++ b/src/es/zonaleros/build.gradle @@ -0,0 +1,20 @@ +ext { + extName = 'Zonaleros' + extClass = '.Zonaleros' + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" + +dependencies { + implementation(project(':lib:uqload-extractor')) + implementation(project(':lib:mp4upload-extractor')) + implementation(project(':lib:okru-extractor')) + implementation(project(':lib:streamtape-extractor')) + implementation(project(':lib:filemoon-extractor')) + implementation(project(':lib:voe-extractor')) + implementation(project(':lib:streamwish-extractor')) + implementation(project(':lib:mixdrop-extractor')) + implementation(project(':lib:dood-extractor')) + implementation(project(':lib:vidhide-extractor')) +} \ No newline at end of file diff --git a/src/es/zonaleros/res/mipmap-hdpi/ic_launcher.png b/src/es/zonaleros/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..93c8fe09 Binary files /dev/null and b/src/es/zonaleros/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/es/zonaleros/res/mipmap-mdpi/ic_launcher.png b/src/es/zonaleros/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..14161994 Binary files /dev/null and b/src/es/zonaleros/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/es/zonaleros/res/mipmap-xhdpi/ic_launcher.png b/src/es/zonaleros/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..b41bef55 Binary files /dev/null and b/src/es/zonaleros/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/es/zonaleros/res/mipmap-xxhdpi/ic_launcher.png b/src/es/zonaleros/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..21e40f11 Binary files /dev/null and b/src/es/zonaleros/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/es/zonaleros/res/mipmap-xxxhdpi/ic_launcher.png b/src/es/zonaleros/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..f6566f89 Binary files /dev/null and b/src/es/zonaleros/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/es/zonaleros/src/eu/kanade/tachiyomi/animeextension/es/zonaleros/Zonaleros.kt b/src/es/zonaleros/src/eu/kanade/tachiyomi/animeextension/es/zonaleros/Zonaleros.kt new file mode 100644 index 00000000..509a10ea --- /dev/null +++ b/src/es/zonaleros/src/eu/kanade/tachiyomi/animeextension/es/zonaleros/Zonaleros.kt @@ -0,0 +1,295 @@ +package eu.kanade.tachiyomi.animeextension.es.zonaleros + +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.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.doodextractor.DoodExtractor +import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor +import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor +import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor +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.uqloadextractor.UqloadExtractor +import eu.kanade.tachiyomi.lib.vidhideextractor.VidHideExtractor +import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.FormBody +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Element +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class Zonaleros : ConfigurableAnimeSource, AnimeHttpSource() { + + override val name = "Zonaleros" + + override val baseUrl = "https://www.zona-leros.com" + + 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 = "1080" + private val QUALITY_LIST = arrayOf("1080", "720", "480", "360") + + private const val PREF_SERVER_KEY = "preferred_server" + private const val PREF_SERVER_DEFAULT = "DoodStream" + private val SERVER_LIST = arrayOf( + "Voe", + "VidHide", + "StreamWish", + "Okru", + "Upload", + "FileLions", + "Filemoon", + "DoodStream", + "MixDrop", + "Streamtape", + "Mp4Upload", + ) + } + + override fun animeDetailsParse(response: Response): SAnime { + val document = response.asJsoup() + val animeDetails = SAnime.create().apply { + title = document.selectFirst("h1.Title")?.text() ?: "" + description = document.selectFirst(".Main section") + ?.select(".Description p")?.drop(1) + ?.dropLast(1)?.joinToString(" ") { it.text() } + genre = document.select(".TxtMAY ul li").joinToString { it.text() } + status = when { + document.location().contains("peliculas") -> SAnime.COMPLETED + else -> SAnime.UNKNOWN + } + artist = document.selectFirst(".ListActors li a")?.text() + document.select(".TxtMAY").map { + when { + it.text().contains("PRODUCTORA") || it.text().contains("CREADOR") -> author = it.nextElementSibling()?.text() + } + } + } + return animeDetails + } + + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/series-h?order=views&page=$page", headers) + + override fun popularAnimeParse(response: Response): AnimesPage { + val document = response.asJsoup() + val elements = document.select(".ListAnimes .Anime > a") + val nextPage = document.select(".pagination [rel=\"next\"]").any() + + val animeList = elements.filter { element -> + element.attr("href").contains("series", ignoreCase = true) || + element.attr("href").contains("peliculas", ignoreCase = true) + }.map { element -> + SAnime.create().apply { + title = element.selectFirst(".Title")!!.text() + thumbnail_url = element.selectFirst("img")?.getImageUrl() + setUrlWithoutDomain(element.attr("abs:href")) + } + } + return AnimesPage(animeList, nextPage) + } + + override fun latestUpdatesParse(response: Response) = popularAnimeParse(response) + + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/peliculas-hd-online-lat?order=published&page=$page", headers) + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val params = ZonalerosFilters.getSearchParameters(filters) + return when { + query.isNotBlank() -> GET("$baseUrl/search?q=$query&page=$page", headers) + params.filter.isNotBlank() -> GET("$baseUrl/${params.getQuery().replaceFirst("&", "?")}&page=$page", headers) + else -> popularAnimeRequest(page) + } + } + + override fun searchAnimeParse(response: Response) = popularAnimeParse(response) + + override fun episodeListParse(response: Response): List<SEpisode> { + val document = response.asJsoup() + return if (document.location().contains("peliculas")) { + listOf( + SEpisode.create().apply { + name = "Película" + episode_number = 1f + setUrlWithoutDomain(document.location()) + }, + ) + } else { + document.select("[id*=temp]").flatMap { season -> + season.select(".ListEpisodios a").reversed().map { + val capText = it.select(".Capi").text() + val seasonNumber = capText.substringBefore("x").trim() + val episodeNumber = capText.substringAfter("x").trim() + SEpisode.create().apply { + name = "T$seasonNumber - Episodio $episodeNumber" + episode_number = episodeNumber.toFloat() + setUrlWithoutDomain(it.attr("abs:href")) + } + } + } + } + } + + override fun videoListParse(response: Response): List<Video> { + val document = response.asJsoup() + val serverDocument = if (document.location().contains("/episode/")) { + document + } else { + val token = document.select("meta[name=\"csrf-token\"]").attr("content") + val calidadId = document.selectFirst("span[data-value]")?.attr("data-value") ?: return emptyList() + val referer = document.location() + + val formBody = FormBody.Builder() + .add("calidad_id", calidadId) + .add("_token", token) + .build() + + val request = Request.Builder() + .url("$baseUrl/api/calidades") + .post(formBody) + .header("accept", "application/json, text/javascript, */*; q=0.01") + .header("accept-language", "es-419,es;q=0.8") + .header("content-type", "application/x-www-form-urlencoded; charset=UTF-8") + .header("origin", baseUrl) + .header("referer", referer) + .header("x-requested-with", "XMLHttpRequest") + .build() + + client.newCall(request).execute().asJsoup() + } + + val scriptServerList = serverDocument.selectFirst("script:containsData(var video)")?.data() ?: return emptyList() + + val videoList = mutableListOf<Video>() + fetchUrls(scriptServerList).filter { it.contains("anomizador", true) }.forEach { + try { + val realUrl = client.newCall(GET(it)).execute() + .networkResponse.toString() + .substringAfter("url=") + .substringBefore("}") + serverVideoResolver(realUrl).also(videoList::addAll) + } catch (e: Exception) { + Log.i("bruh e", e.toString()) + } + } + return videoList + } + + private fun fetchUrls(text: String?): List<String> { + if (text.isNullOrEmpty()) return listOf() + val linkRegex = "(http|ftp|https):\\/\\/([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-])".toRegex() + return linkRegex.findAll(text).map { it.value.trim().removeSurrounding("\"") }.toList() + } + + override fun getFilterList(): AnimeFilterList = ZonalerosFilters.FILTER_LIST + + private val voeExtractor by lazy { VoeExtractor(client) } + private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) } + private val filemoonExtractor by lazy { FilemoonExtractor(client) } + private val mixdropExtractor by lazy { MixDropExtractor(client) } + private val doodExtractor by lazy { DoodExtractor(client) } + private val streamTapeExtractor by lazy { StreamTapeExtractor(client) } + private val uqloadExtractor by lazy { UqloadExtractor(client) } + private val okruExtractor by lazy { OkruExtractor(client) } + private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) } + private val vidHideExtractor by lazy { VidHideExtractor(client, headers) } + + private fun serverVideoResolver(url: String): List<Video> { + val embedUrl = url.lowercase() + return when { + embedUrl.contains("voe") -> voeExtractor.videosFromUrl(url) + embedUrl.contains("uqload") -> uqloadExtractor.videosFromUrl(url) + embedUrl.contains("ok.ru") || embedUrl.contains("okru") -> okruExtractor.videosFromUrl(url) + 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("filelions") || embedUrl.contains("lion") -> streamwishExtractor.videosFromUrl(url, videoNameGen = { "FileLions:$it" }) + embedUrl.contains("mp4upload") || embedUrl.contains("mp4") -> mp4uploadExtractor.videosFromUrl(url, headers) + embedUrl.contains("vidhide") || embedUrl.contains("vid.") || embedUrl.contains("nika") -> vidHideExtractor.videosFromUrl(url) { "VidHide:$it" } + embedUrl.contains("mix") -> mixdropExtractor.videosFromUrl(url) + else -> emptyList() + } + } + + 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() + } + + private fun Element.getImageUrl(): String? { + return when { + isValidUrl("data-src") -> attr("abs:data-src") + isValidUrl("data-lazy-src") -> attr("abs:data-lazy-src") + isValidUrl("srcset") -> attr("abs:srcset").substringBefore(" ") + isValidUrl("src") -> attr("abs:src") + else -> "" + } + } + + private fun Element.isValidUrl(attrName: String): Boolean { + if (!hasAttr(attrName)) return false + return !attr(attrName).contains("data:image/") + } + + 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) + } +} diff --git a/src/es/zonaleros/src/eu/kanade/tachiyomi/animeextension/es/zonaleros/ZonalerosDto.kt b/src/es/zonaleros/src/eu/kanade/tachiyomi/animeextension/es/zonaleros/ZonalerosDto.kt new file mode 100644 index 00000000..58a608cc --- /dev/null +++ b/src/es/zonaleros/src/eu/kanade/tachiyomi/animeextension/es/zonaleros/ZonalerosDto.kt @@ -0,0 +1,29 @@ +package eu.kanade.tachiyomi.animeextension.es.zonaleros + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class EpisodesDto( + @SerialName("paginate_url") var paginateUrl: String? = null, + @SerialName("perpage") var perpage: Double? = null, + @SerialName("eps") var eps: ArrayList<Eps> = arrayListOf(), +) + +@Serializable +data class Eps( + @SerialName("num") var num: Int? = null, +) + +@Serializable +data class EpisodeInfoDto( + @SerialName("default") var default: String? = null, + @SerialName("caps") var caps: ArrayList<Caps> = arrayListOf(), +) + +@Serializable +data class Caps( + @SerialName("episodio") var episodio: Int? = null, + @SerialName("url") var url: String? = null, + @SerialName("thumb") var thumb: String? = null, +) diff --git a/src/es/zonaleros/src/eu/kanade/tachiyomi/animeextension/es/zonaleros/ZonalerosFilters.kt b/src/es/zonaleros/src/eu/kanade/tachiyomi/animeextension/es/zonaleros/ZonalerosFilters.kt new file mode 100644 index 00000000..f7bb7a51 --- /dev/null +++ b/src/es/zonaleros/src/eu/kanade/tachiyomi/animeextension/es/zonaleros/ZonalerosFilters.kt @@ -0,0 +1,147 @@ +package eu.kanade.tachiyomi.animeextension.es.zonaleros + +import eu.kanade.tachiyomi.animesource.model.AnimeFilter +import eu.kanade.tachiyomi.animesource.model.AnimeFilterList +import java.util.Calendar + +object ZonalerosFilters { + 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 { "" } + + fun toUriPart() = vals[state].second.takeIf { it.isNotEmpty() }?.let { 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.asUriPart(): String { + return (this.getFirst<R>() as QueryPartFilter).toUriPart() + } + + 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.asUriPart<TypesFilter>() + + filters.parseCheckbox<GenresFilter>(ZonalerosFiltersData.GENRES, "generos") + + filters.parseCheckbox<YearsFilter>(ZonalerosFiltersData.YEARS, "year") + + filters.parseCheckbox<StatusFilter>(ZonalerosFiltersData.STATUS, "estado") + + filters.asQueryPart<SortFilter>("order"), + ) + } + + val FILTER_LIST get() = AnimeFilterList( + AnimeFilter.Header("La busqueda por texto ignora el filtro"), + TypesFilter(), + GenresFilter(), + YearsFilter(), + StatusFilter(), + SortFilter(), + ) + + class TypesFilter : QueryPartFilter("Tipo", ZonalerosFiltersData.TYPES) + + class GenresFilter : CheckBoxFilterList("Género", ZonalerosFiltersData.GENRES.map { CheckBoxVal(it.first, false) }) + + class YearsFilter : CheckBoxFilterList("Año", ZonalerosFiltersData.YEARS.map { CheckBoxVal(it.first, false) }) + + class StatusFilter : CheckBoxFilterList("Estado", ZonalerosFiltersData.STATUS.map { CheckBoxVal(it.first, false) }) + + class SortFilter : QueryPartFilter("Orden", ZonalerosFiltersData.SORT) + + private object ZonalerosFiltersData { + val TYPES = arrayOf( + Pair("Películas", "peliculas-hd-online-lat"), + Pair("Series", "series-h"), + ) + + val GENRES = arrayOf( + Pair("Accíon", "1"), + Pair("Animación", "15"), + Pair("Anime", "22"), + Pair("Artes marciales", "25"), + Pair("Aventura", "2"), + Pair("Biográfica", "24"), + Pair("Ciencia Ficcion", "3"), + Pair("Comedia", "4"), + Pair("Crimen", "14"), + Pair("Deportes", "19"), + Pair("Documental", "27"), + Pair("Drama", "5"), + Pair("Eroticos", "32"), + Pair("Familiar", "26"), + Pair("Fantasia", "6"), + Pair("Fantasía", "16"), + Pair("Guerra", "20"), + Pair("Hechos Reales", "23"), + Pair("Humor negro", "29"), + Pair("Infantil", "7"), + Pair("Misterio", "10"), + Pair("Musical", "18"), + Pair("Navidad", "21"), + Pair("Romance", "12"), + Pair("Sci-Fi", "30"), + Pair("Superhéroes", "28"), + Pair("Suspenso", "8"), + Pair("Terror", "9"), + Pair("Wéstern", "17"), + Pair("Zombies", "13"), + ) + + val YEARS = (1990..Calendar.getInstance().get(Calendar.YEAR)).map { Pair("$it", "$it") }.reversed().toTypedArray() + + val STATUS = arrayOf( + Pair("Estreno", "Estreno"), + Pair("Resubido", "Resubido"), + Pair("Actualizado", "Actualizado"), + Pair("Nueva Calidad", "Nueva Calidad"), + ) + + val SORT = arrayOf( + Pair("Por Defecto", "created"), + Pair("Recientemente Actualizados", "updated"), + Pair("Fecha de lanzamiento", "published"), + Pair("Nombre A-Z", "titleaz"), + Pair("Nombre Z-A", "titleza"), + Pair("Calificación", "rating"), + Pair("Vistas", "views"), + ) + } +} diff --git a/src/pt/animesotaku/build.gradle b/src/pt/animesotaku/build.gradle index 4031a00f..60877e1b 100644 --- a/src/pt/animesotaku/build.gradle +++ b/src/pt/animesotaku/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'AnimesOtaku' extClass = '.AnimesOtaku' - extVersionCode = 1 + extVersionCode = 2 } apply from: "$rootDir/common.gradle" diff --git a/src/pt/animesotaku/src/eu/kanade/tachiyomi/animeextension/pt/animesotaku/AnimesOtaku.kt b/src/pt/animesotaku/src/eu/kanade/tachiyomi/animeextension/pt/animesotaku/AnimesOtaku.kt index 91c07458..dedfc4c9 100644 --- a/src/pt/animesotaku/src/eu/kanade/tachiyomi/animeextension/pt/animesotaku/AnimesOtaku.kt +++ b/src/pt/animesotaku/src/eu/kanade/tachiyomi/animeextension/pt/animesotaku/AnimesOtaku.kt @@ -35,7 +35,7 @@ class AnimesOtaku : AnimeHttpSource() { override val baseUrl = "https://www.animesotaku.cc" - override val lang = "pt" + override val lang = "pt-BR" override val supportsLatest = true diff --git a/src/zh/xfani/build.gradle b/src/zh/xfani/build.gradle index 395a0366..89b38f74 100644 --- a/src/zh/xfani/build.gradle +++ b/src/zh/xfani/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Xfani' extClass = '.Xfani' - extVersionCode = 4 + extVersionCode = 5 } apply from: "$rootDir/common.gradle" diff --git a/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt index 5c210961..92583f32 100644 --- a/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt +++ b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt @@ -136,9 +136,8 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource { SEpisode.create().apply { name = it.text() url = it.attr("href") - episode_number = numberRegex.find(name)?.value?.toFloat() ?: -1F } - }.sortedByDescending { it.episode_number } + }.reversed() } override fun videoListParse(response: Response): List<Video> { @@ -155,9 +154,11 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource { val currentEpisodeName = allEpisodeElements.firstNotNullOfOrNull { elements -> elements.firstOrNull { it.attr("href") == currentPath }?.select("span")?.text() } - val targetEpisodeNumber = currentEpisodeName?.let { numberRegex.find(it)?.value?.toIntOrNull() } ?: -1 + val targetEpisodeNumber = + currentEpisodeName?.let { numberRegex.find(it)?.value?.toIntOrNull() } ?: -1 val sourceList = allEpisodeElements.map { elements -> - elements.findSourceOrNull { name, _ -> numberRegex.find(name)?.value?.toIntOrNull() == targetEpisodeNumber } + elements.findSourceOrNull { name, _ -> name == currentEpisodeName } + ?: elements.findSourceOrNull { name, _ -> numberRegex.find(name)?.value?.toIntOrNull() == targetEpisodeNumber } ?: elements.findSourceOrNull { _, url -> url.endsWith(currentEpisodePathName) } } val sourceNameList = document.select(".anthology-tab .swiper-wrapper a").map {