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