diff --git a/src/es/doramasyt/build.gradle b/src/es/doramasyt/build.gradle index 5423041d..f73ca3a5 100644 --- a/src/es/doramasyt/build.gradle +++ b/src/es/doramasyt/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Doramasyt' extClass = '.Doramasyt' - extVersionCode = 13 + extVersionCode = 14 } apply from: "$rootDir/common.gradle" @@ -10,4 +10,9 @@ dependencies { implementation(project(':lib:uqload-extractor')) implementation(project(':lib:streamtape-extractor')) implementation(project(':lib:okru-extractor')) + implementation(project(':lib:voe-extractor')) + implementation(project(':lib:filemoon-extractor')) + implementation(project(':lib:streamwish-extractor')) + implementation(project(':lib:dood-extractor')) + implementation(project(':lib:mixdrop-extractor')) } diff --git a/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/Doramasyt.kt b/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/Doramasyt.kt index d3774bc6..762c2f86 100644 --- a/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/Doramasyt.kt +++ b/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/Doramasyt.kt @@ -2,34 +2,41 @@ package eu.kanade.tachiyomi.animeextension.es.doramasyt import android.app.Application import android.content.SharedPreferences -import android.util.Log +import android.util.Base64 import androidx.preference.ListPreference import androidx.preference.PreferenceScreen -import eu.kanade.tachiyomi.animeextension.es.doramasyt.extractors.SolidFilesExtractor 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.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.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.voeextractor.VoeExtractor import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.util.asJsoup +import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking +import eu.kanade.tachiyomi.util.parseAs +import okhttp3.FormBody 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.math.ceil -class Doramasyt : ConfigurableAnimeSource, ParsedAnimeHttpSource() { +class Doramasyt : ConfigurableAnimeSource, AnimeHttpSource() { override val name = "Doramasyt" - override val baseUrl = "https://doramasyt.com" + override val baseUrl = "https://www.doramasyt.com" override val lang = "es" @@ -39,241 +46,203 @@ class Doramasyt : ConfigurableAnimeSource, ParsedAnimeHttpSource() { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) } - override fun popularAnimeSelector(): String = "div.col-lg-2.col-md-4.col-6 div.animes" + 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") - override fun popularAnimeRequest(page: Int): Request = GET("https://doramasyt.com/doramas/?p=$page") - - override fun popularAnimeFromElement(element: Element): SAnime { - val anime = SAnime.create() - anime.setUrlWithoutDomain( - element.select("div.anithumb a").attr("href"), + private const val PREF_SERVER_KEY = "preferred_server" + private const val PREF_SERVER_DEFAULT = "Filemoon" + private val SERVER_LIST = arrayOf( + "Voe", + "StreamWish", + "Okru", + "Upload", + "FileLions", + "Filemoon", + "DoodStream", + "MixDrop", + "Streamtape", ) - anime.title = element.select("div.animedtls p").text() - anime.thumbnail_url = element.select(" div.anithumb a img").attr("src") - anime.description = element.select("div.animedtls p").text() - return anime } - override fun popularAnimeNextPageSelector(): String = "ul.pagination li:last-child a" + override fun animeDetailsParse(response: Response): SAnime { + val document = response.asJsoup() + val animeDetails = SAnime.create().apply { + title = document.selectFirst(".flex-column h1.text-capitalize")?.text() ?: "" + description = document.selectFirst(".h-100 .mb-3 p")?.text() + genre = document.select(".lh-lg span").joinToString { it.text() } + thumbnail_url = document.selectFirst(".gap-3 img")?.getImageUrl() + status = document.select(".lh-sm .ms-2").eachText().let { items -> + when { + items.any { it.contains("Finalizado") } -> SAnime.COMPLETED + items.any { it.contains("Estreno") } -> SAnime.ONGOING + else -> SAnime.UNKNOWN + } + } + } + return animeDetails + } + + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/doramas?p=$page", headers) + + override fun popularAnimeParse(response: Response): AnimesPage { + val document = response.asJsoup() + val elements = document.select(".ficha_efecto a") + val nextPage = document.select(".pagination [rel=\"next\"]").any() + val animeList = elements.map { element -> + SAnime.create().apply { + title = element.selectFirst(".title_cap")!!.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/emision?p=$page", headers) + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val params = DoramasytFilters.getSearchParameters(filters) + return when { + query.isNotBlank() -> GET("$baseUrl/buscar?q=$query", headers) + params.filter.isNotBlank() -> GET("$baseUrl/doramas${params.getQuery()}&p=$page", headers) + else -> popularAnimeRequest(page) + } + } + + override fun searchAnimeParse(response: Response) = popularAnimeParse(response) override fun episodeListParse(response: Response): List<SEpisode> { - return super.episodeListParse(response).reversed() + val document = response.asJsoup() + val token = document.select("meta[name='csrf-token']").attr("content") + val capListLink = document.select(".caplist").attr("data-ajax") + val referer = document.location() + + val detail = getEpisodeDetails(capListLink, token, referer) + val total = detail.eps.size + val perPage = detail.perpage ?: return emptyList() + val pages = (total / perPage).ceilPage() + + return (1..pages).parallelCatchingFlatMapBlocking { + getEpisodePage(detail.paginateUrl ?: "", it, token, referer).caps.mapIndexed { idx, ep -> + val episodeNumber = (ep.episodio ?: (idx + 1)) + SEpisode.create().apply { + name = "Capítulo $episodeNumber" + episode_number = episodeNumber.toFloat() + setUrlWithoutDomain(ep.url ?: "") + } + } + }.reversed() } - override fun episodeListSelector(): String = "div.mainrowdiv.pagesdiv div.jpage div.col-item" + private fun getEpisodeDetails(capListLink: String, token: String, referer: String): EpisodesDto { + val formBody = FormBody.Builder().add("_token", token).build() + val request = Request.Builder() + .url(capListLink) + .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() - override fun episodeFromElement(element: Element): SEpisode { - val episode = SEpisode.create() - val epNum = getNumberFromEpsString(element.select("a div.flimss div.dtlsflim p").text()) - Log.i("bruh ep", element.select("a").attr("href")) - val formatedEp = when { - epNum.isNotEmpty() -> epNum.toFloatOrNull() ?: 1F - else -> 1F - } - episode.setUrlWithoutDomain(element.select("a").attr("href")) - episode.episode_number = formatedEp - episode.name = "Episodio $formatedEp" - - return episode + return client.newCall(request).execute().parseAs<EpisodesDto>() } - private fun getNumberFromEpsString(epsStr: String): String { - return epsStr.filter { it.isDigit() } + private fun getEpisodePage(paginateUrl: String, page: Int, token: String, referer: String): EpisodeInfoDto { + val formBodyEp = FormBody.Builder() + .add("_token", token) + .add("p", "$page") + .build() + val requestEp = Request.Builder() + .url(paginateUrl) + .post(formBodyEp) + .header("accept", "application/json, text/javascript, */*; q=0.01") + .header("accept-language", "es-419,es;q=0.8") + .header("content-type", "application/x-www-form-urlencoded; charset=UTF-8") + .header("origin", baseUrl) + .header("referer", referer) + .header("x-requested-with", "XMLHttpRequest") + .build() + + return client.newCall(requestEp).execute().parseAs<EpisodeInfoDto>() } override fun videoListParse(response: Response): List<Video> { val document = response.asJsoup() - val videoList = mutableListOf<Video>() - document.select("div.playermain ul.dropcaps li#play-video a.cap").forEach { players -> - val server = players.text() - val urlEncoded = players.attr("data-player") - val byte = android.util.Base64.decode(urlEncoded, android.util.Base64.DEFAULT) - val url = String(byte, charset("UTF-8")).substringAfter("?url=") - if (server.contains("streamtape")) { - val video = StreamTapeExtractor(client).videoFromUrl(url) - if (video != null) { - videoList.add(video) - } - } - if (server.contains("ok")) { - val videos = OkruExtractor(client).videosFromUrl(url) - videoList.addAll(videos) - } - if (server.contains("zeus")) { - val videos = SolidFilesExtractor(client).videosFromUrl(url) - videoList.addAll(videos) - } - if (server.contains("uqload") || server.contains("upload")) { - videoList.addAll(UqloadExtractor(client).videosFromUrl(url)) - } - } - return videoList + return document.select("[data-player]") + .map { String(Base64.decode(it.attr("data-player"), Base64.DEFAULT)) } + .parallelCatchingFlatMapBlocking { serverVideoResolver(it) } } - override fun videoListSelector() = throw UnsupportedOperationException() + override fun getFilterList(): AnimeFilterList = DoramasytFilters.FILTER_LIST - override fun videoUrlParse(document: Document) = throw UnsupportedOperationException() + 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) } - override fun videoFromElement(element: Element) = throw UnsupportedOperationException() + 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("mix") -> mixdropExtractor.videosFromUrl(url) + else -> emptyList() + } + } override fun List<Video>.sort(): List<Video> { - return try { - val videoSorted = this.sortedWith( - compareBy<Video> { it.quality.replace("[0-9]".toRegex(), "") }.thenByDescending { getNumberFromString(it.quality) }, - ).toTypedArray() - val userPreferredQuality = preferences.getString("preferred_quality", "Okru:720p") - val preferredIdx = videoSorted.indexOfFirst { x -> x.quality == userPreferredQuality } - if (preferredIdx != -1) { - videoSorted.drop(preferredIdx + 1) - videoSorted[0] = videoSorted[preferredIdx] - } - videoSorted.toList() - } catch (e: Exception) { - this - } + 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 getNumberFromString(epsStr: String): String { - return epsStr.filter { it.isDigit() }.ifEmpty { "0" } - } - - 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 - + private fun Element.getImageUrl(): String? { return when { - query.isNotBlank() -> GET("$baseUrl/buscar?q=$query&p=$page", headers) - genreFilter.state != 0 -> GET("$baseUrl/doramas?categoria=false&genero=${genreFilter.toUriPart()}&fecha=false&letra=false&p=$page") - else -> GET("$baseUrl/doramas/?p=$page") + 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 -> "" } } - override fun searchAnimeFromElement(element: Element): SAnime { - val anime = SAnime.create() - anime.setUrlWithoutDomain( - element.selectFirst("a")!!.attr("href"), - ) - anime.title = element.select("div.animedtls p").text() - anime.thumbnail_url = element.select("a img").attr("src") - anime.description = element.select("div.animedtls p").text() - return anime + private fun Element.isValidUrl(attrName: String): Boolean { + if (!hasAttr(attrName)) return false + return !attr(attrName).contains("anime.png") } - 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.herohead div.heroheadmain")!!.attr("style").substringAfter(",url(").substringBefore(") no-repeat;") - val sub = document.selectFirst("div.herohead div.heroheadmain strong")!!.text() - val title = document.selectFirst("div.herohead div.heroheadmain h1")!!.text().trim() - anime.title = title + if (sub.isNotEmpty()) " ($sub)" else "" - anime.description = document.selectFirst("div.herohead div.heroheadmain div.flimdtls p.textComplete")!! - .text().removeSurrounding("\"").replace("Ver menos", "") - anime.genre = document.select("div.herohead div.heroheadmain div.writersdiv div.nobel h6 a").joinToString { it.text() } - anime.status = parseStatus(document.select("div.herohead div.heroheadmain div.writersdiv div.state h6").text()) - return anime - } - - private fun parseStatus(statusString: String): Int { - return when { - statusString.contains("Estreno") -> SAnime.ONGOING - statusString.contains("Finalizado") -> SAnime.COMPLETED - else -> SAnime.UNKNOWN - } - } - - override fun latestUpdatesNextPageSelector() = "" // popularAnimeNextPageSelector() - - override fun latestUpdatesFromElement(element: Element): SAnime { - val anime = SAnime.create() - anime.setUrlWithoutDomain( - element.selectFirst("a")!!.attr("href"), - ) - anime.title = element.selectFirst("a div.chapter p")!!.text() - anime.thumbnail_url = element.select("a div.chapter img").attr("src") - anime.description = element.select("div.animedtls p").text() - return anime - } - - override fun latestUpdatesRequest(page: Int) = GET(baseUrl) - - override fun latestUpdatesSelector() = "div.heroarea div.heromain div.chapters div.row div.chaps" // popularAnimeSelector() - - override fun getFilterList(): AnimeFilterList = AnimeFilterList( - AnimeFilter.Header("La busqueda por texto ignora el filtro"), - GenreFilter(), - ) - - private class GenreFilter : UriPartFilter( - "Generos", - arrayOf( - Pair("<selecionar>", "false"), - Pair("Acción", "accion"), - Pair("Amistad", "amistad"), - Pair("Artes marciales", "artes-marciales"), - Pair("Aventuras", "aventuras"), - Pair("Bélico", "belico"), - Pair("C-Drama", "c-drama"), - Pair("Ciencia Ficción", "ciencia-ficcion"), - Pair("Comedia", "comedia"), - Pair("Comida", "comida"), - Pair("Crimen ", "crimen"), - Pair("Deporte", "deporte"), - Pair("Documental", "documental"), - Pair("Drama", "drama"), - Pair("Escolar", "escolar"), - Pair("Familiar", "familiar"), - Pair("Fantasia", "fantasia"), - Pair("Histórico", "historico"), - Pair("HK-Drama", "hk-drama"), - Pair("Horror", "horror"), - Pair("Idols", "idols"), - Pair("J-Drama", "j-drama"), - Pair("Juvenil", "juvenil"), - Pair("K-Drama", "k-drama"), - Pair("Legal", "legal"), - Pair("Médico", "medico"), - Pair("Melodrama", "melodrama"), - Pair("Militar", "militar"), - Pair("Misterio", "misterio"), - Pair("Musical", "musical"), - Pair("Negocios", "negocios"), - Pair("Policial", "policial"), - Pair("Política", "politica"), - Pair("Psicológico", "psicologico"), - Pair("Reality Show", "reality-show"), - Pair("Recuentos de la vida", "recuentos-de-la-vida"), - Pair("Romance", "romance"), - Pair("Sobrenatural", "sobrenatural"), - Pair("Supervivencia", "supervivencia"), - Pair("Suspenso", "suspenso"), - Pair("Thai-Drama", "thai-drama"), - Pair("Thriller", "thriller"), - Pair("Time Travel", "time-travel"), - Pair("TW-Drama", "tw-drama"), - Pair("Yaoi", "yaoi"), - Pair("Yuri", "yuri"), - ), - ) - - 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 - } + private fun Double.ceilPage(): Int = if (this % 1 == 0.0) this.toInt() else ceil(this).toInt() override fun setupPreferenceScreen(screen: PreferenceScreen) { - val qualities = arrayOf( - "Okru:1080p", "Okru:720p", "Okru:480p", "Okru:360p", "Okru:240p", "Okru:144p", // Okru - "Uqload", "SolidFiles", "StreamTape", // video servers without resolution - ) - val videoQualityPref = ListPreference(screen.context).apply { - key = "preferred_quality" - title = "Preferred quality" - entries = qualities - entryValues = qualities - setDefaultValue("Okru:720p") + 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 -> @@ -282,7 +251,22 @@ class Doramasyt : ConfigurableAnimeSource, ParsedAnimeHttpSource() { val entry = entryValues[index] as String preferences.edit().putString(key, entry).commit() } - } - screen.addPreference(videoQualityPref) + }.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/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/DoramasytDto.kt b/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/DoramasytDto.kt new file mode 100644 index 00000000..3cba8883 --- /dev/null +++ b/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/DoramasytDto.kt @@ -0,0 +1,29 @@ +package eu.kanade.tachiyomi.animeextension.es.doramasyt + +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/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/DoramasytFilters.kt b/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/DoramasytFilters.kt new file mode 100644 index 00000000..87d9641f --- /dev/null +++ b/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/DoramasytFilters.kt @@ -0,0 +1,117 @@ +package eu.kanade.tachiyomi.animeextension.es.doramasyt + +import eu.kanade.tachiyomi.animesource.model.AnimeFilter +import eu.kanade.tachiyomi.animesource.model.AnimeFilterList + +object DoramasytFilters { + 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 { "" } + } + + 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.asQueryPart<CategoriesFilter>("categoria") + + filters.asQueryPart<GenresFilter>("genero") + + filters.asQueryPart<YearsFilter>("fecha") + + filters.asQueryPart<LettersFilter>("letra"), + ) + } + + val FILTER_LIST get() = AnimeFilterList( + AnimeFilter.Header("La busqueda por texto ignora el filtro"), + CategoriesFilter(), + GenresFilter(), + YearsFilter(), + LettersFilter(), + ) + + class CategoriesFilter : QueryPartFilter("Categoría", DoramasytFiltersData.CATEGORIES) + + class GenresFilter : QueryPartFilter("Género", DoramasytFiltersData.GENRES) + + class YearsFilter : QueryPartFilter("Año", DoramasytFiltersData.YEARS) + + class LettersFilter : QueryPartFilter("Letra", DoramasytFiltersData.LETTER) + + private object DoramasytFiltersData { + val CATEGORIES = arrayOf( + Pair("<Selecionar>", ""), + Pair("Dorama", "dorama"), + Pair("Live Action", "live-action"), + Pair("Pelicula", "pelicula"), + Pair("Series Turcas", "serie-turcas"), + ) + + val YEARS = arrayOf(Pair("<Seleccionar>", "")) + (1982..2024).map { Pair("$it", "$it") }.reversed().toTypedArray() + + val LETTER = arrayOf(Pair("<Seleccionar>", "")) + ('A'..'Z').map { Pair("$it", "$it") }.toTypedArray() + + val GENRES = arrayOf( + Pair("<Selecionar>", ""), + Pair("Policial", "policial"), + Pair("Romance", "romance"), + Pair("Comedia", "comedia"), + Pair("Escolar", "escolar"), + Pair("Acción", "accion"), + Pair("Thriller", "thriller"), + Pair("Drama", "drama"), + Pair("Misterio", "misterio"), + Pair("Fantasia", "fantasia"), + Pair("Histórico", "historico"), + Pair("Bélico", "belico"), + Pair("Militar", "militar"), + Pair("Médico", "medico"), + Pair("Ciencia Ficción", "ciencia-ficcion"), + Pair("Sobrenatural", "sobrenatural"), + Pair("Horror", "horror"), + Pair("Política", "politica"), + Pair("Familiar", "familiar"), + Pair("Melodrama", "melodrama"), + Pair("Deporte", "deporte"), + Pair("Comida", "comida"), + Pair("Supervivencia", "supervivencia"), + Pair("Aventuras", "aventuras"), + Pair("Artes marciales", "artes-marciales"), + Pair("Recuentos de la vida", "recuentos-de-la-vida"), + Pair("Amistad", "amistad"), + Pair("Psicológico", "psicologico"), + Pair("Yuri", "yuri"), + Pair("K-Drama", "k-drama"), + Pair("J-Drama", "j-drama"), + Pair("C-Drama", "c-drama"), + Pair("HK-Drama", "hk-drama"), + Pair("TW-Drama", "tw-drama"), + Pair("Thai-Drama", "thai-drama"), + Pair("Idols", "idols"), + Pair("Suspenso", "suspenso"), + Pair("Negocios", "negocios"), + Pair("Time Travel", "time-travel"), + Pair("Crimen ", "crimen"), + Pair("Yaoi", "yaoi"), + Pair("Legal", "legal"), + Pair("Juvenil", "juvenil"), + Pair("Musical", "musical"), + Pair("Reality Show", "reality-show"), + Pair("Documental", "documental"), + Pair("Turcas", "turcas"), + ) + } +} diff --git a/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/extractors/SolidFilesExtractor.kt b/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/extractors/SolidFilesExtractor.kt deleted file mode 100644 index ccb634e6..00000000 --- a/src/es/doramasyt/src/eu/kanade/tachiyomi/animeextension/es/doramasyt/extractors/SolidFilesExtractor.kt +++ /dev/null @@ -1,27 +0,0 @@ -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 - } - } -}