diff --git a/lib/amazon-extractor/build.gradle.kts b/lib/amazon-extractor/build.gradle.kts
new file mode 100644
index 00000000..a503203d
--- /dev/null
+++ b/lib/amazon-extractor/build.gradle.kts
@@ -0,0 +1,7 @@
+plugins {
+    id("lib-android")
+}
+
+dependencies {
+    implementation(project(":lib:playlist-utils"))
+}
\ No newline at end of file
diff --git a/lib/amazon-extractor/src/main/java/eu/kanade/tachiyomi/lib/amazonextractor/AmazonExtractor.kt b/lib/amazon-extractor/src/main/java/eu/kanade/tachiyomi/lib/amazonextractor/AmazonExtractor.kt
new file mode 100644
index 00000000..3402cb9e
--- /dev/null
+++ b/lib/amazon-extractor/src/main/java/eu/kanade/tachiyomi/lib/amazonextractor/AmazonExtractor.kt
@@ -0,0 +1,41 @@
+package eu.kanade.tachiyomi.lib.amazonextractor
+
+import eu.kanade.tachiyomi.animesource.model.Video
+import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.util.asJsoup
+import okhttp3.OkHttpClient
+
+class AmazonExtractor(private val client: OkHttpClient) {
+
+    private val playlistUtils by lazy { PlaylistUtils(client) }
+
+    fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
+        if (url.contains("disable", true)) return emptyList()
+
+        val document = client.newCall(GET(url)).execute().asJsoup()
+
+        val shareIdScript = document.select("script:containsData(var shareId)").firstOrNull()?.data()
+        if (shareIdScript.isNullOrBlank()) return emptyList()
+
+        val shareId = shareIdScript.substringAfter("shareId = \"").substringBefore("\"")
+        val amazonApiJsonUrl = "https://www.amazon.com/drive/v1/shares/$shareId?resourceVersion=V2&ContentType=JSON&asset=ALL"
+
+        val amazonApiJson = client.newCall(GET(amazonApiJsonUrl)).execute().asJsoup()
+
+        val epId = amazonApiJson.toString().substringAfter("\"id\":\"").substringBefore("\"")
+        val amazonApiUrl = "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"
+
+        val amazonApi = client.newCall(GET(amazonApiUrl)).execute().asJsoup()
+
+        val videoUrl = amazonApi.toString().substringAfter("\"FOLDER\":").substringAfter("tempLink\":\"").substringBefore("\"")
+
+        val serverName = if (videoUrl.contains("&ext=es")) "AmazonES" else "Amazon"
+
+        return if (videoUrl.contains(".m3u8")) {
+            playlistUtils.extractFromHls(videoUrl, videoNameGen = { "${prefix}$serverName:$it" })
+        } else {
+            listOf(Video(videoUrl, "${prefix}$serverName", videoUrl))
+        }
+    }
+}
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/all/animeonsen/res/web_hi_res_512.png b/src/all/animeonsen/res/web_hi_res_512.png
deleted file mode 100644
index 2ca8cc98..00000000
Binary files a/src/all/animeonsen/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/all/animeworldindia/build.gradle b/src/all/animeworldindia/build.gradle
index 2e6be89d..0c887191 100644
--- a/src/all/animeworldindia/build.gradle
+++ b/src/all/animeworldindia/build.gradle
@@ -1,11 +1,11 @@
 ext {
     extName = 'AnimeWorld India'
     extClass = '.AnimeWorldIndiaFactory'
-    extVersionCode = 14
+    extVersionCode = 15
 }
 
 apply from: "$rootDir/common.gradle"
 
 dependencies {
     implementation(project(":lib:playlist-utils"))
-}
\ No newline at end of file
+}
diff --git a/src/all/animeworldindia/src/eu/kanade/tachiyomi/animeextension/all/animeworldindia/AnimeWorldIndia.kt b/src/all/animeworldindia/src/eu/kanade/tachiyomi/animeextension/all/animeworldindia/AnimeWorldIndia.kt
index 4ab6cd81..5fbe3c37 100644
--- a/src/all/animeworldindia/src/eu/kanade/tachiyomi/animeextension/all/animeworldindia/AnimeWorldIndia.kt
+++ b/src/all/animeworldindia/src/eu/kanade/tachiyomi/animeextension/all/animeworldindia/AnimeWorldIndia.kt
@@ -29,7 +29,7 @@ class AnimeWorldIndia(
 
     override val name = "AnimeWorld India"
 
-    override val baseUrl = "https://anime-world.in"
+    override val baseUrl = "https://anime-world.co"
 
     override val supportsLatest = true
 
diff --git a/src/all/animeworldindia/src/eu/kanade/tachiyomi/animeextension/all/animeworldindia/AnimeWorldIndiaFilters.kt b/src/all/animeworldindia/src/eu/kanade/tachiyomi/animeextension/all/animeworldindia/AnimeWorldIndiaFilters.kt
index 5baf4062..a55518bd 100644
--- a/src/all/animeworldindia/src/eu/kanade/tachiyomi/animeextension/all/animeworldindia/AnimeWorldIndiaFilters.kt
+++ b/src/all/animeworldindia/src/eu/kanade/tachiyomi/animeextension/all/animeworldindia/AnimeWorldIndiaFilters.kt
@@ -47,6 +47,7 @@ class AnimeWorldIndiaFilters {
 
     private fun getYearList() = listOf(
         StringQuery("Any", "all"),
+        StringQuery("2025", "2025"),
         StringQuery("2024", "2024"),
         StringQuery("2023", "2023"),
         StringQuery("2022", "2022"),
diff --git a/src/all/chineseanime/res/web_hi_res_512.png b/src/all/chineseanime/res/web_hi_res_512.png
deleted file mode 100644
index d8572ff1..00000000
Binary files a/src/all/chineseanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/all/javguru/res/web_hi_res_512.png b/src/all/javguru/res/web_hi_res_512.png
deleted file mode 100644
index 1e1e735b..00000000
Binary files a/src/all/javguru/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/all/missav/res/web_hi_res_512.png b/src/all/missav/res/web_hi_res_512.png
deleted file mode 100644
index 2b9568fd..00000000
Binary files a/src/all/missav/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/all/shabakatycinemana/build.gradle b/src/all/shabakatycinemana/build.gradle
new file mode 100644
index 00000000..f01ac7ca
--- /dev/null
+++ b/src/all/shabakatycinemana/build.gradle
@@ -0,0 +1,11 @@
+ext {
+    extName = 'ShabakatyCinemana'
+    extClass = '.ShabakatyCinemana'
+    extVersionCode = 2
+    isNsfw = false
+}
+
+apply from: "$rootDir/common.gradle"
+
+dependencies {
+}
\ No newline at end of file
diff --git a/src/all/shabakatycinemana/res/mipmap-hdpi/ic_launcher.png b/src/all/shabakatycinemana/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..c151c1af
Binary files /dev/null and b/src/all/shabakatycinemana/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/all/shabakatycinemana/res/mipmap-mdpi/ic_launcher.png b/src/all/shabakatycinemana/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..e6165950
Binary files /dev/null and b/src/all/shabakatycinemana/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/all/shabakatycinemana/res/mipmap-xhdpi/ic_launcher.png b/src/all/shabakatycinemana/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..ad6d8cda
Binary files /dev/null and b/src/all/shabakatycinemana/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/all/shabakatycinemana/res/mipmap-xxhdpi/ic_launcher.png b/src/all/shabakatycinemana/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..b021a32b
Binary files /dev/null and b/src/all/shabakatycinemana/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/all/shabakatycinemana/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/shabakatycinemana/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..3748b4bd
Binary files /dev/null and b/src/all/shabakatycinemana/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/all/shabakatycinemana/src/eu/kanade/tachiyomi/animeextension/all/shabakatycinemana/ShabakatyCinemana.kt b/src/all/shabakatycinemana/src/eu/kanade/tachiyomi/animeextension/all/shabakatycinemana/ShabakatyCinemana.kt
new file mode 100644
index 00000000..5370f8b9
--- /dev/null
+++ b/src/all/shabakatycinemana/src/eu/kanade/tachiyomi/animeextension/all/shabakatycinemana/ShabakatyCinemana.kt
@@ -0,0 +1,629 @@
+package eu.kanade.tachiyomi.animeextension.all.shabakatycinemana
+
+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.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.Track
+import eu.kanade.tachiyomi.animesource.model.Video
+import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.util.parseAs
+import kotlinx.serialization.DeserializationStrategy
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.descriptors.buildClassSerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.JsonDecoder
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.int
+import kotlinx.serialization.json.jsonArray
+import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.Request
+import okhttp3.Response
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+import java.text.SimpleDateFormat
+import java.util.Calendar
+import java.util.Locale
+
+inline fun <reified T> Response.asModel(deserializer: DeserializationStrategy<T>): T {
+    return Json.decodeFromString(deserializer, this.body.string())
+}
+
+inline fun <reified T> Response.asModelList(deserializer: DeserializationStrategy<T>): List<T> {
+    return Json.parseToJsonElement(this.body.string()).jsonArray.map {
+        Json.decodeFromJsonElement(deserializer, it)
+    }
+}
+
+object SEpisodeDeserializer : DeserializationStrategy<SEpisode> {
+    override val descriptor: SerialDescriptor
+        get() = buildClassSerialDescriptor("SEpisode")
+
+    override fun deserialize(decoder: Decoder): SEpisode {
+        val jsonDecoder = decoder as JsonDecoder
+        val jsonObject = jsonDecoder.decodeJsonElement() as JsonObject
+
+        val nb = jsonObject["nb"]?.jsonPrimitive?.content!!
+        val episodeNumber = jsonObject["episodeNummer"]?.jsonPrimitive?.content
+        val seasonNumber = jsonObject["season"]?.jsonPrimitive?.content
+        val seasonEpisode = arrayOf(seasonNumber, episodeNumber).joinToString(ShabakatyCinemana.SEASON_EPISODE_DELIMITER)
+        val uploadDate = jsonObject["videoUploadDate"]?.jsonPrimitive?.content.runCatching {
+            this?.let { ShabakatyCinemana.DATE_FORMATTER.parse(it)?.time }
+        }.getOrNull() ?: 0L
+
+        return SEpisode.create().apply {
+            url = nb
+            episode_number = "$seasonNumber.$episodeNumber".parseAs()
+            name = seasonEpisode
+            date_upload = uploadDate
+        }
+    }
+}
+
+object VideoDeserializer : DeserializationStrategy<Video> {
+    override val descriptor: SerialDescriptor
+        get() = buildClassSerialDescriptor("Video")
+
+    override fun deserialize(decoder: Decoder): Video {
+        val jsonDecoder = decoder as JsonDecoder
+        val jsonObject = jsonDecoder.decodeJsonElement() as JsonObject
+
+        val videoUrl = jsonObject["videoUrl"]?.jsonPrimitive?.content!!
+        val quality = jsonObject["resolution"]?.jsonPrimitive?.content.orEmpty()
+
+        return Video(url = videoUrl, videoUrl = videoUrl, quality = quality)
+    }
+}
+
+object SubtitleDeserialize : DeserializationStrategy<List<Track>> {
+    override val descriptor: SerialDescriptor
+        get() = buildClassSerialDescriptor("Track")
+
+    override fun deserialize(decoder: Decoder): List<Track> {
+        val jsonDecoder = decoder as JsonDecoder
+        val jsonObject = jsonDecoder.decodeJsonElement() as JsonObject
+
+        return jsonObject["translations"]?.jsonArray?.map {
+            val url = it.jsonObject["file"]?.jsonPrimitive?.content!!
+            val name = it.jsonObject["name"]?.jsonPrimitive?.content
+            val extension = it.jsonObject["extention"]?.jsonPrimitive?.content
+            val lang = arrayOf(name, extension).joinToString(ShabakatyCinemana.SUBTITLE_DELIMITER)
+
+            Track(url, lang)
+        }.orEmpty()
+    }
+}
+
+data class SAnimeListWithInfo(val animes: List<SAnime>, val offset: Int)
+
+object SAnimeWithInfoDeserializer : DeserializationStrategy<SAnimeListWithInfo> {
+    override val descriptor: SerialDescriptor
+        get() = buildClassSerialDescriptor("SAnimeListWithInfo")
+
+    override fun deserialize(decoder: Decoder): SAnimeListWithInfo {
+        val jsonDecoder = decoder as JsonDecoder
+        val jsonObject = jsonDecoder.decodeJsonElement() as JsonObject
+
+        val animeList = jsonObject["info"]?.jsonArray?.map {
+            Json.decodeFromJsonElement(SAnimeDeserializer, it)
+        }.orEmpty()
+        val offset = jsonObject["offset"]?.jsonPrimitive?.int ?: 0
+
+        return SAnimeListWithInfo(animeList, offset)
+    }
+}
+
+object SAnimeDeserializer : DeserializationStrategy<SAnime> {
+    override val descriptor: SerialDescriptor
+        get() = buildClassSerialDescriptor("SAnime")
+
+    override fun deserialize(decoder: Decoder): SAnime {
+        val jsonDecoder = decoder as JsonDecoder
+        val jsonObject = jsonDecoder.decodeJsonElement() as JsonObject
+
+        val nb = jsonObject["nb"]?.jsonPrimitive?.content!!
+        val enTitle = jsonObject["en_title"]?.jsonPrimitive?.content ?: "no title"
+        val imgObjUrl = jsonObject["imgObjUrl"]?.jsonPrimitive?.content
+        val categories = jsonObject["categories"]?.jsonArray?.map {
+            it.jsonObject["en_title"]?.jsonPrimitive?.content
+        }?.joinToString(", ")
+        val enContent = jsonObject["en_content"]?.jsonPrimitive?.content
+        val year = jsonObject["year"]?.jsonPrimitive?.content ?: "N/A"
+        val stars = jsonObject["stars"]?.jsonPrimitive?.content?.parseAs<Float>()?.toInt() ?: 0
+        val starsText = "${"★".repeat(stars / 2)}${"☆".repeat(5 - (stars / 2))}"
+        val likes = jsonObject["Likes"]?.jsonPrimitive?.content?.parseAs<Int>() ?: 0
+        val dislikes = jsonObject["DisLikes"]?.jsonPrimitive?.content?.parseAs<Int>() ?: 0
+//        val ref = jsonObject["imdbUrlRef"]?.jsonPrimitive?.content ?: ""
+
+        return SAnime.create().apply {
+            url = nb
+            title = enTitle
+            thumbnail_url = imgObjUrl
+            genre = categories
+            description = "$year | $starsText | $likes\uD83D\uDC4D  $dislikes\uD83D\uDC4E\n\n$enContent"
+        }
+    }
+}
+
+class ShabakatyCinemana : ConfigurableAnimeSource, AnimeHttpSource() {
+
+    override val name = "Shabakaty Cinemana"
+
+    override val baseUrl = "https://cinemana.shabakaty.com"
+
+    private val apiBaseUrl = "$baseUrl/api/android"
+
+    override val lang = "all"
+
+    override val supportsLatest = true
+
+    private val preferences: SharedPreferences by lazy {
+        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
+    }
+
+    companion object {
+        private const val IS_BROWSING_FILTER_NAME = "Browse"
+        private const val KIND_FILTER_NAME = "Kind"
+        private const val MAIN_CATEGORY_FILTER_NAME = "Main Category"
+        private const val SUB_CATEGORY_FILTER_NAME = "Sub Category"
+        private const val LANGUAGE_FILTER_NAME = "Language"
+        private const val YEAR_FILTER_NAME = "Year"
+        private const val BROWSE_RESULT_SORT_FILTER = "Browse Sort"
+
+        private const val POPULAR_ITEMS_PER_PAGE = 30
+        private const val SEARCH_ITEMS_PER_PAGE = 12
+        private const val LATEST_ITEMS_PER_PAGE = 24
+
+        private const val PREF_LATEST_KIND_KEY = "preferred_latest_kind"
+        private const val PREF_LATEST_KIND_DEFAULT = "Movies"
+        private val KINDS_LIST = arrayOf(
+            Pair("Movies", 1),
+            Pair("Series", 2),
+        )
+
+        private const val PREF_QUALITY_KEY = "preferred_quality"
+        private const val PREF_QUALITY_DEFAULT = "1080"
+        private val QUALITY_LIST = arrayOf("2160", "1080", "720", "480", "360", "240")
+
+        private const val PREF_SUBTITLE_LANG_KEY = "preferred_subtitle_language"
+        private const val PREF_SUBTITLE_LANG_DEFAULT = "arabic"
+        private val LANG_LIST = arrayOf("arabic", "english")
+
+        private const val PREF_SUBTITLE_EXT_KEY = "preferred_subtitle_extension"
+        private const val PREF_SUBTITLE_EXT_DEFAULT = "ass"
+        private val EXT_LIST = arrayOf("srt", "vtt", "ass")
+
+        const val SUBTITLE_DELIMITER = " - "
+        const val SEASON_EPISODE_DELIMITER = " - "
+
+        val DATE_FORMATTER by lazy {
+            SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH)
+        }
+    }
+
+    override fun getAnimeUrl(anime: SAnime) = "$baseUrl/video/en/${anime.url}"
+
+    override fun animeDetailsRequest(anime: SAnime) = GET("$apiBaseUrl/allVideoInfo/id/${anime.url}")
+
+    override fun animeDetailsParse(response: Response) = response.asModel(SAnimeDeserializer)
+
+    override fun latestUpdatesRequest(page: Int): Request {
+        val kind = preferences.getString(PREF_LATEST_KIND_KEY, PREF_LATEST_KIND_DEFAULT)!!
+
+        return GET("$apiBaseUrl/latest$kind/level/0/itemsPerPage/$LATEST_ITEMS_PER_PAGE/page/${page - 1}/", headers)
+    }
+
+    override fun latestUpdatesParse(response: Response): AnimesPage {
+        val animeList = response.asModelList(SAnimeDeserializer)
+        return AnimesPage(animeList, animeList.size == LATEST_ITEMS_PER_PAGE)
+    }
+
+    override fun popularAnimeRequest(page: Int): Request {
+        val kindPref = preferences.getString(PREF_LATEST_KIND_KEY, PREF_LATEST_KIND_DEFAULT)!!
+        val kind = KINDS_LIST.first { it.first == kindPref }.second
+
+        val url = "$apiBaseUrl/video/V/2/itemsPerPage/$POPULAR_ITEMS_PER_PAGE/level/0/videoKind/$kind/sortParam/desc/pageNumber/${page - 1}"
+        return GET(url, headers)
+    }
+
+    override fun popularAnimeParse(response: Response): AnimesPage {
+        val animeList = response.asModelList(SAnimeDeserializer)
+        return AnimesPage(animeList, animeList.size == POPULAR_ITEMS_PER_PAGE)
+    }
+
+    override suspend fun getSearchAnime(
+        page: Int,
+        query: String,
+        filters: AnimeFilterList,
+    ): AnimesPage {
+        val filterList = if (filters.isEmpty()) getFilterList() else filters
+        val isBrowsingFilter = filterList.find { it.name == IS_BROWSING_FILTER_NAME } as CheckBoxFilter
+        val kindFilter = filterList.find { it.name == KIND_FILTER_NAME } as SingleSelectFilter
+        val mainCategoryFilter = filterList.find { it.name == MAIN_CATEGORY_FILTER_NAME } as MultipleSelectFilter
+        val subCategoryFilter = filterList.find { it.name == SUB_CATEGORY_FILTER_NAME } as MultipleSelectFilter
+        val languageFilter = filterList.find { it.name == LANGUAGE_FILTER_NAME } as SingleSelectFilter
+        val yearFilter = filterList.find { it.name == YEAR_FILTER_NAME } as YearFilter
+        val browseResultSortFilter = filterList.find { it.name == BROWSE_RESULT_SORT_FILTER } as BrowseResultSort
+        val isBrowsing = isBrowsingFilter.state
+        val kindName = kindFilter.getNameValue()
+        val kindNumber = kindFilter.getNumberValue().toString()
+        val selectedMainCategories = mainCategoryFilter.getSelectedIds()
+        val mainCategory = selectedMainCategories.joinToString(",")
+        val selectedSubCategories = subCategoryFilter.getSelectedIds()
+        val bothCategory = (selectedMainCategories + selectedSubCategories).joinToString(",")
+        val language = languageFilter.getNumberValue().toString()
+        val year = yearFilter.getFormatted()
+        val browseResultSort = browseResultSortFilter.getValue()
+
+        var url = apiBaseUrl.toHttpUrl()
+        if (isBrowsing) {
+            if (languageFilter.state != 0 && mainCategory.isNotBlank()) {
+                url = url.newBuilder()
+                    .addPathSegment("videosByCategoryAndLanguage")
+                    .addQueryParameter("language_id", language)
+                    .addQueryParameter("category_id", mainCategory)
+                    .build()
+            } else {
+                url = url.newBuilder()
+                    .addPathSegment("videosByCategory")
+                    .build()
+
+                if (mainCategoryFilter.hasSelected()) {
+                    url = url.newBuilder().addQueryParameter("categoryID", mainCategory).build()
+                }
+            }
+
+            url = url.newBuilder()
+                .addQueryParameter("level", "0")
+                .addQueryParameter("offset", "${(page - 1) * POPULAR_ITEMS_PER_PAGE}")
+                .addQueryParameter("videoKind", kindNumber)
+                .addQueryParameter("orderby", browseResultSort)
+                .build()
+
+            val resp = client.newCall(GET(url, headers)).execute()
+            // Todo: remove SAnimeWithInfo data class if no longer needed
+            val animeListWithInfo = resp.asModel(SAnimeWithInfoDeserializer)
+            return AnimesPage(animeListWithInfo.animes, animeListWithInfo.animes.size == POPULAR_ITEMS_PER_PAGE)
+        } else {
+//            star=8&year=1900,2025
+            url = url.newBuilder()
+                .addQueryParameter("level", "0")
+                .addPathSegment("AdvancedSearch")
+                .addQueryParameter("type", kindName)
+                .addQueryParameter("page", "${page - 1}")
+                .addQueryParameter("year", year)
+                .build()
+
+            if (bothCategory.isNotBlank()) {
+                url = url.newBuilder().addQueryParameter("category_id", bothCategory).build()
+            }
+
+            if (query.isNotBlank()) {
+                url = url.newBuilder()
+                    .addQueryParameter("videoTitle", query)
+                    .addQueryParameter("staffTitle", query)
+                    .build()
+            }
+
+            val resp = client.newCall(GET(url, headers)).execute()
+            val animeList = resp.asModelList(SAnimeDeserializer)
+            return AnimesPage(animeList, animeList.size == SEARCH_ITEMS_PER_PAGE)
+        }
+    }
+
+    override fun searchAnimeParse(response: Response) =
+        throw UnsupportedOperationException("Not used.")
+
+    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) =
+        throw UnsupportedOperationException("Not used.")
+
+    override suspend fun getEpisodeList(anime: SAnime): List<SEpisode> {
+        val episodeList = super.getEpisodeList(anime)
+
+        if (episodeList.isNotEmpty()) {
+            return episodeList.sortedWith(
+                compareBy(
+                    { it.name.split(SEASON_EPISODE_DELIMITER).first().parseAs<Int>() },
+                    { it.name.split(SEASON_EPISODE_DELIMITER).last().parseAs<Int>() },
+                ),
+            ).reversed()
+        } else {
+            return listOf(
+                SEpisode.create().apply {
+                    url = anime.url
+                    episode_number = 1.0F
+                    name = "movie"
+                },
+            )
+        }
+    }
+
+    override fun episodeListRequest(anime: SAnime): Request = GET("$apiBaseUrl/videoSeason/id/${anime.url}")
+
+    override fun episodeListParse(response: Response) = response.asModelList(SEpisodeDeserializer)
+
+    override suspend fun getVideoList(episode: SEpisode): List<Video> {
+        val extension = preferences.getString(PREF_SUBTITLE_EXT_KEY, PREF_SUBTITLE_EXT_DEFAULT)!!
+        val language = preferences.getString(PREF_SUBTITLE_LANG_KEY, PREF_SUBTITLE_LANG_DEFAULT)!!
+        val subs = this.client.newCall(GET("$apiBaseUrl/translationFiles/id/${episode.url}")).execute()
+            .asModel(SubtitleDeserialize)
+            .sortedWith(
+                compareBy(
+                    { it.lang.split(SUBTITLE_DELIMITER).contains(extension) },
+                    { it.lang.split(SUBTITLE_DELIMITER).contains(language) },
+                ),
+            ).reversed()
+
+        return super.getVideoList(episode).map {
+            Video(url = it.url, quality = it.quality, videoUrl = it.videoUrl, subtitleTracks = subs)
+        }
+    }
+
+    override fun videoListRequest(episode: SEpisode) = GET("$apiBaseUrl/transcoddedFiles/id/${episode.url}")
+
+    override fun videoListParse(response: Response) = response.asModelList(VideoDeserializer)
+
+    override fun List<Video>.sort(): List<Video> {
+        val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
+        return this.sortedWith(
+            compareBy(
+                { it.quality.contains(quality) },
+                { Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
+            ),
+        ).reversed()
+    }
+
+    private open class SingleSelectFilter(displayName: String, val vals: Array<Pair<String, Int>>, default: Int = 0) :
+        AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray(), default) {
+        fun getNameValue() = vals[state].first.lowercase()
+        fun getNumberValue() = vals[state].second
+    }
+
+    private open class MultipleSelectFilter(displayName: String, vals: Array<Pair<String, Int>>) :
+        AnimeFilter.Group<CheckBoxFilter>(displayName, vals.map { CheckBoxFilter(it.first, false, it.second) }) {
+        fun getSelectedIds(): List<Int> =
+            this.state.filter { it.state }.map { it.value }
+        fun hasSelected(): Boolean = this.state.any { it.state }
+    }
+
+    private open class CheckBoxFilter(displayName: String, default: Boolean, val value: Int = 0) : AnimeFilter.CheckBox(displayName, default)
+
+    private open class YearFilter(displayName: String, years: Pair<YearTextFilter, YearTextFilter>) : AnimeFilter.Group<YearTextFilter>(
+        displayName,
+        years.toList(),
+    ) {
+        fun getFormatted(): String = this.state.map {
+            it.state.ifBlank { it.default }
+        }.joinToString(",")
+    }
+
+    private open class YearTextFilter(displayName: String, val default: String) : AnimeFilter.Text(displayName, default)
+
+    private open class BrowseResultSort(
+        displayName: String,
+        val vals: Array<Pair<String, Pair<String, String>>>,
+        val default: Selection = Selection(0, false),
+    ) : AnimeFilter.Sort(displayName, vals.map { it.first }.toTypedArray(), default) {
+        fun getValue(): String {
+            val currentState = state ?: default
+            val sortKind = vals[currentState.index].second
+            return if (currentState.ascending) {
+                sortKind.first
+            } else {
+                sortKind.second
+            }
+        }
+    }
+
+    override fun getFilterList() = AnimeFilterList(
+        AnimeFilter.Header("Filter Search Result"),
+        CheckBoxFilter(IS_BROWSING_FILTER_NAME, false),
+        SingleSelectFilter(
+            KIND_FILTER_NAME,
+            KINDS_LIST,
+        ),
+        MultipleSelectFilter(
+            MAIN_CATEGORY_FILTER_NAME,
+            arrayOf(
+                Pair("Action", 84),
+                Pair("Adventure", 56),
+                Pair("Animation", 57),
+                Pair("Comedy", 59),
+                Pair("Crime", 60),
+                Pair("Documentary", 61),
+                Pair("Drama", 62),
+                Pair("Fantasy", 67),
+                Pair("Horror", 70),
+                Pair("Mystery", 76),
+                Pair("Romance", 77),
+                Pair("Sci-Fi", 78),
+                Pair("Sport", 79),
+                Pair("Thriller", 80),
+                Pair("Western", 89),
+            ),
+        ),
+        MultipleSelectFilter(
+            SUB_CATEGORY_FILTER_NAME,
+            arrayOf(
+                Pair("Biography", 58),
+                Pair("Family", 65),
+                Pair("History", 68),
+                Pair("Musical", 75),
+                Pair("War", 81),
+                Pair("Supernatural", 87),
+                Pair("Music", 88),
+                Pair("Talk-Show", 90),
+                Pair("Short", 97),
+                Pair("Reality-TV", 101),
+                Pair("Arabic dubbed", 102),
+                Pair("News", 104),
+                Pair("Ecchi", 105),
+                Pair("Film-Noir", 106),
+                Pair("Game", 111),
+                Pair("Psychological", 112),
+                Pair("Slice of Life", 113),
+                Pair("Game-Show", 118),
+                Pair("Magic", 123),
+                Pair("Super Power", 124),
+                Pair("Seinen", 125),
+                Pair("Shounen", 126),
+                Pair("School", 127),
+                Pair("Sports", 128),
+                Pair("Iraqi", 130),
+            ),
+        ),
+        SingleSelectFilter(
+            LANGUAGE_FILTER_NAME,
+            arrayOf(
+                Pair("", 0),
+                Pair("English", 7),
+                Pair("Arabic", 9),
+                Pair("Hindi", 10),
+                Pair("French", 11),
+                Pair("German", SEARCH_ITEMS_PER_PAGE),
+                Pair("Italian", 13),
+                Pair("Spanish", 14),
+                Pair("Chinese", 21),
+                Pair("Japanese", 22),
+                Pair("Korean", 23),
+                Pair("Russian", LATEST_ITEMS_PER_PAGE),
+                Pair("Turkish", 25),
+                Pair("Norwegian", 26),
+                Pair("Persian", 27),
+                Pair("Swedish", 35),
+                Pair("Hungary", 36),
+                Pair("Polish", 38),
+                Pair("Dutch", 39),
+                Pair("Portuguese", 40),
+                Pair("Indonesian", 41),
+                Pair("Danish", 43),
+                Pair("Romania", 44),
+                Pair("Ukrainian", 48),
+                Pair("Mandarin", 52),
+                Pair("Catalan", 65),
+                Pair("Filipino", 68),
+                Pair("Hungarian", 76),
+                Pair("Thai", 80),
+                Pair("Croatian", 84),
+                Pair(" Malay", 85),
+                Pair("Finnish", 86),
+                Pair("Vietnamese", 88),
+                Pair("Zulu", 89),
+                Pair("Taiwan", 47),
+                Pair("Bulgarian", 95),
+                Pair("Serbian", 97),
+                Pair("Greek", 28),
+                Pair("Finland", 37),
+                Pair("Iran", 42),
+                Pair("Hebrew", 46),
+                Pair("Icelandic", 56),
+                Pair("Georgian", 58),
+                Pair("Pakistani", 61),
+                Pair("Czeck", 72),
+                Pair("Latvian", 87),
+                Pair("Kazakh", 90),
+                Pair("Estonian", 91),
+                Pair("Quechua", 92),
+                Pair("Multi Language", 93),
+                Pair("Papiamento", 94),
+                Pair("Albanian", 100),
+                Pair("Slovenian", 103),
+                Pair("Macedonian", 109),
+                Pair("Kurdish", 112),
+                Pair("Irish", 115),
+                Pair("Afghani", 106),
+            ),
+        ),
+        YearFilter(
+            YEAR_FILTER_NAME,
+            YearTextFilter("start", "1900") to
+                YearTextFilter("end", Calendar.getInstance().get(Calendar.YEAR).toString()),
+        ),
+        BrowseResultSort(
+            BROWSE_RESULT_SORT_FILTER,
+            arrayOf(
+                Pair("Upload", Pair("asc", "desc")),
+                Pair("Release", Pair("r_asc", "r_desc")),
+                Pair("Name", Pair("title_asc", "title_desc")),
+                Pair("View", Pair("views_asc", "views_desc")),
+                Pair("Age Rating", Pair("rating_asc", "rating_desc")),
+            ),
+        ),
+    )
+
+    override fun setupPreferenceScreen(screen: PreferenceScreen) {
+        ListPreference(screen.context).apply {
+            key = PREF_LATEST_KIND_KEY
+            title = "Preferred Latest kind"
+            entries = KINDS_LIST.map { it.first }.toTypedArray()
+            entryValues = KINDS_LIST.map { it.first }.toTypedArray()
+            setDefaultValue(PREF_LATEST_KIND_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)
+
+        ListPreference(screen.context).apply {
+            key = PREF_SUBTITLE_LANG_KEY
+            title = "Preferred Subtitle Language"
+            entries = LANG_LIST
+            entryValues = LANG_LIST
+            setDefaultValue(PREF_SUBTITLE_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()
+            }
+        }.also(screen::addPreference)
+
+        ListPreference(screen.context).apply {
+            key = PREF_SUBTITLE_EXT_KEY
+            title = "Preferred Subtitle Extension"
+            entries = EXT_LIST
+            entryValues = EXT_LIST
+            setDefaultValue(PREF_SUBTITLE_EXT_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/all/torrentioanime/build.gradle b/src/all/torrentioanime/build.gradle
index 52ec219c..258789ef 100644
--- a/src/all/torrentioanime/build.gradle
+++ b/src/all/torrentioanime/build.gradle
@@ -1,7 +1,7 @@
 ext {
     extName = 'Torrentio Anime (Torrent / Debrid)'
     extClass = '.Torrentio'
-    extVersionCode = 14
+    extVersionCode = 15
     containsNsfw = false
 }
 
diff --git a/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/AniListQueries.kt b/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/AniListQueries.kt
index 9eb474e0..f2925ab2 100644
--- a/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/AniListQueries.kt
+++ b/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/AniListQueries.kt
@@ -29,7 +29,8 @@ fun anilistQuery() = """
                 startDate_like: %year,
                 seasonYear: %seasonYear,
                 season: %season,
-                format_in: %format
+                format_in: %format,
+                isAdult: false
             ) {
                 id
                 title {
@@ -103,7 +104,7 @@ fun anilistLatestQuery() = """
 
 fun getDetailsQuery() = """
 query media(%id: Int) {
-  Media(id: %id) {
+  Media(id: %id, isAdult: false) {
     id
     title {
         romaji
@@ -137,23 +138,3 @@ query media(%id: Int) {
   }
 }
 """.toQuery()
-
-fun getEpisodeQuery() = """
-query media(%id: Int, %type: MediaType) {
-  Media(id: %id, type: %type) {
-    episodes
-    nextAiringEpisode {
-      episode
-    }
-  }
-}
-""".toQuery()
-
-fun getMalIdQuery() = """
-query media(%id: Int, %type: MediaType) {
-  Media(id: %id, type: %type) {
-    idMal
-    id
-  }
-}
-""".toQuery()
diff --git a/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/Torrentio.kt b/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/Torrentio.kt
index 007f9cc6..021cf57f 100644
--- a/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/Torrentio.kt
+++ b/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/Torrentio.kt
@@ -10,10 +10,10 @@ import androidx.preference.ListPreference
 import androidx.preference.MultiSelectListPreference
 import androidx.preference.PreferenceScreen
 import androidx.preference.SwitchPreferenceCompat
+import eu.kanade.tachiyomi.animeextension.all.torrentioanime.dto.AniZipResponse
 import eu.kanade.tachiyomi.animeextension.all.torrentioanime.dto.AnilistMeta
 import eu.kanade.tachiyomi.animeextension.all.torrentioanime.dto.AnilistMetaLatest
 import eu.kanade.tachiyomi.animeextension.all.torrentioanime.dto.DetailsById
-import eu.kanade.tachiyomi.animeextension.all.torrentioanime.dto.EpisodeList
 import eu.kanade.tachiyomi.animeextension.all.torrentioanime.dto.StreamDataTorrent
 import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
 import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
@@ -66,6 +66,7 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
             .add("query", query)
             .add("variables", variables)
             .build()
+
         return POST("https://graphql.anilist.co", body = requestBody)
     }
 
@@ -148,7 +149,8 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
 
     override fun popularAnimeParse(response: Response): AnimesPage {
         val jsonData = response.body.string()
-        return parseSearchJson(jsonData) }
+        return parseSearchJson(jsonData)
+    }
 
     // =============================== Latest ===============================
     override fun latestUpdatesRequest(page: Int): Request {
@@ -300,41 +302,55 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
 
     // ============================== Episodes ==============================
     override fun episodeListRequest(anime: SAnime): Request {
-        return GET("https://anime-kitsu.strem.fun/meta/series/anilist%3A${anime.url}.json")
+        return GET("https://api.ani.zip/mappings?anilist_id=${anime.url}")
     }
 
     override fun episodeListParse(response: Response): List<SEpisode> {
         val responseString = response.body.string()
-        val episodeList = json.decodeFromString<EpisodeList>(responseString)
+        val aniZipResponse = json.decodeFromString<AniZipResponse>(responseString)
 
-        return when (episodeList.meta?.type) {
-            "series" -> {
-                episodeList.meta.videos
-                    ?.let { videos ->
-                        if (preferences.getBoolean(UPCOMING_EP_KEY, UPCOMING_EP_DEFAULT)) { videos } else { videos.filter { video -> (video.released?.let { parseDate(it) } ?: 0L) <= System.currentTimeMillis() } }
+        return when (aniZipResponse.mappings?.type) {
+            "TV" -> {
+                aniZipResponse.episodes
+                    ?.let { episodes ->
+                        if (preferences.getBoolean(UPCOMING_EP_KEY, UPCOMING_EP_DEFAULT)) {
+                            episodes
+                        } else {
+                            episodes.filter { (_, episode) -> (episode?.airDate?.let { parseDate(it) } ?: 0L) <= System.currentTimeMillis() }
+                        }
                     }
-                    ?.map { video ->
+                    ?.mapNotNull { (_, episode) ->
+                        val episodeNumber = runCatching { episode?.episode?.toFloat() }.getOrNull()
+
+                        if (episodeNumber == null) {
+                            return@mapNotNull null
+                        }
+
+                        val title = episode?.title?.get("en")
+
                         SEpisode.create().apply {
-                            episode_number = video.episode?.toFloat() ?: 0.0F
-                            url = "/stream/series/${video.videoId}.json"
-                            date_upload = video.released?.let { parseDate(it) } ?: 0L
-                            name = "Episode ${video.episode} : ${
-                                video.title?.removePrefix("Episode ")
-                                    ?.replaceFirst("\\d+\\s*".toRegex(), "")
-                                    ?.trim()
-                            }"
-                            scanlator = (video.released?.let { parseDate(it) } ?: 0L).takeIf { it > System.currentTimeMillis() }?.let { "Upcoming" } ?: ""
+                            episode_number = episodeNumber
+                            url = "/stream/series/kitsu:${aniZipResponse.mappings.kitsuId}:${String.format(Locale.ENGLISH, "%.0f", episodeNumber)}.json"
+                            date_upload = episode?.airDate?.let { parseDate(it) } ?: 0L
+                            name = if (title == null) "Episode ${episode?.episode}" else "Episode ${episode.episode}: $title"
+                            scanlator = (episode?.airDate?.let { parseDate(it) } ?: 0L).takeIf { it > System.currentTimeMillis() }?.let { "Upcoming" } ?: ""
                         }
                     }.orEmpty().reversed()
             }
 
-            "movie" -> {
-                // Handle movie response
+            "MOVIE" -> {
+                val dateUpload = if (!aniZipResponse.episodes.isNullOrEmpty()) {
+                    aniZipResponse.episodes["1"]?.airDate?.let { parseDate(it) } ?: 0L
+                } else {
+                    0L
+                }
+
                 listOf(
                     SEpisode.create().apply {
                         episode_number = 1.0F
-                        url = "/stream/movie/${episodeList.meta.kitsuId}.json"
+                        url = "/stream/movie/kitsu:${aniZipResponse.mappings.kitsuId}.json"
                         name = "Movie"
+                        date_upload = dateUpload
                     },
                 ).reversed()
             }
@@ -342,6 +358,12 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
             else -> emptyList()
         }
     }
+
+    private fun parseDateTime(dateStr: String): Long {
+        return runCatching { DATE_TIME_FORMATTER.parse(dateStr)?.time }
+            .getOrNull() ?: 0L
+    }
+
     private fun parseDate(dateStr: String): Long {
         return runCatching { DATE_FORMATTER.parse(dateStr)?.time }
             .getOrNull() ?: 0L
@@ -421,6 +443,7 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
             udp://www.torrent.eu.org:451/announce,
             ${fetchTrackers().split("\n").joinToString(",")}
         """.trimIndent()
+
         return streamList.streams?.map { stream ->
             val urlOrHash =
                 if (debridProvider == "none") {
@@ -875,8 +898,12 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
         private const val IS_EFFICIENT_KEY = "efficient"
         private const val IS_EFFICIENT_DEFAULT = false
 
-        private val DATE_FORMATTER by lazy {
+        private val DATE_TIME_FORMATTER by lazy {
             SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH)
         }
+
+        private val DATE_FORMATTER by lazy {
+            SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
+        }
     }
 }
diff --git a/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/dto/AniZipDto.kt b/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/dto/AniZipDto.kt
new file mode 100644
index 00000000..b555f3f2
--- /dev/null
+++ b/src/all/torrentioanime/src/eu/kanade/tachiyomi/animeextension/all/torrentioanime/dto/AniZipDto.kt
@@ -0,0 +1,67 @@
+package eu.kanade.tachiyomi.animeextension.all.torrentioanime.dto
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class AniZipResponse(
+    val titles: Map<String, String?>? = null,
+    val episodes: Map<String, AniZipEpisode?>? = null,
+    val episodeCount: Int? = null,
+    val specialCount: Int? = null,
+    val images: List<AniZipImage?>? = null,
+    val mappings: AniZipMappings? = null,
+)
+
+@Serializable
+data class AniZipEpisode(
+    val episode: String? = null,
+    val episodeNumber: Int? = null,
+    val absoluteEpisodeNumber: Int? = null,
+    val seasonNumber: Int? = null,
+    val title: Map<String, String?>? = null,
+    val length: Int? = null,
+    val runtime: Int? = null,
+    @SerialName("airdate")
+    val airDate: String? = null,
+    val rating: String? = null,
+    @SerialName("anidbEid")
+    val aniDbEpisodeId: Long? = null,
+    val tvdbShowId: Long? = null,
+    val tvdbId: Long? = null,
+    val overview: String? = null,
+    val image: String? = null,
+)
+
+@Serializable
+data class AniZipImage(
+    val coverType: String? = null,
+    val url: String? = null,
+)
+
+@Serializable
+data class AniZipMappings(
+    @SerialName("animeplanet_id")
+    val animePlanetId: String? = null,
+    @SerialName("kitsu_id")
+    val kitsuId: Long? = null,
+    @SerialName("mal_id")
+    val myAnimeListId: Long? = null,
+    val type: String? = null,
+    @SerialName("anilist_id")
+    val aniListId: Long? = null,
+    @SerialName("anisearch_id")
+    val aniSearchId: Long? = null,
+    @SerialName("anidb_id")
+    val aniDbId: Long? = null,
+    @SerialName("notifymoe_id")
+    val notifyMoeId: String? = null,
+    @SerialName("livechart_id")
+    val liveChartId: Long? = null,
+    @SerialName("thetvdb_id")
+    val theTvDbId: Long? = null,
+    @SerialName("imdb_id")
+    val imdbId: String? = null,
+    @SerialName("themoviedb_id")
+    val theMovieDbId: String? = null,
+)
diff --git a/src/ar/akwam/res/play_store_512.png b/src/ar/akwam/res/play_store_512.png
deleted file mode 100644
index 17a4ebb9..00000000
Binary files a/src/ar/akwam/res/play_store_512.png and /dev/null differ
diff --git a/src/ar/anime4up/res/play_store_512.png b/src/ar/anime4up/res/play_store_512.png
deleted file mode 100644
index 9bfe8e48..00000000
Binary files a/src/ar/anime4up/res/play_store_512.png and /dev/null differ
diff --git a/src/ar/animeblkom/res/web_hi_res_512.png b/src/ar/animeblkom/res/web_hi_res_512.png
deleted file mode 100644
index b47c05e9..00000000
Binary files a/src/ar/animeblkom/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/animeiat/res/web_hi_res_512.png b/src/ar/animeiat/res/web_hi_res_512.png
deleted file mode 100644
index 06a6eece..00000000
Binary files a/src/ar/animeiat/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/animelek/res/play_store_512.png b/src/ar/animelek/res/play_store_512.png
deleted file mode 100644
index 13283b86..00000000
Binary files a/src/ar/animelek/res/play_store_512.png and /dev/null differ
diff --git a/src/ar/animerco/res/play_store_512.png b/src/ar/animerco/res/play_store_512.png
deleted file mode 100644
index fbf8380e..00000000
Binary files a/src/ar/animerco/res/play_store_512.png and /dev/null differ
diff --git a/src/ar/arabanime/res/web_hi_res_512.png b/src/ar/arabanime/res/web_hi_res_512.png
deleted file mode 100644
index 684ce74b..00000000
Binary files a/src/ar/arabanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/arabseed/res/web_hi_res_512.png b/src/ar/arabseed/res/web_hi_res_512.png
deleted file mode 100644
index 42b8ffbf..00000000
Binary files a/src/ar/arabseed/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/asia2tv/res/play_store_512.png b/src/ar/asia2tv/res/play_store_512.png
deleted file mode 100644
index 644679a6..00000000
Binary files a/src/ar/asia2tv/res/play_store_512.png and /dev/null differ
diff --git a/src/ar/egydead/res/web_hi_res_512.png b/src/ar/egydead/res/web_hi_res_512.png
deleted file mode 100644
index bcff8301..00000000
Binary files a/src/ar/egydead/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/faselhd/res/web_hi_res_512.png b/src/ar/faselhd/res/web_hi_res_512.png
deleted file mode 100644
index bbe17f6d..00000000
Binary files a/src/ar/faselhd/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/mycima/res/web_hi_res_512.png b/src/ar/mycima/res/web_hi_res_512.png
deleted file mode 100644
index b3d75682..00000000
Binary files a/src/ar/mycima/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/tuktukcinema/res/web_hi_res_512.png b/src/ar/tuktukcinema/res/web_hi_res_512.png
deleted file mode 100644
index 55c57feb..00000000
Binary files a/src/ar/tuktukcinema/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/witanime/res/web_hi_res_512.png b/src/ar/witanime/res/web_hi_res_512.png
deleted file mode 100644
index e9de882c..00000000
Binary files a/src/ar/witanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/xsanime/res/web_hi_res_512.png b/src/ar/xsanime/res/web_hi_res_512.png
deleted file mode 100644
index b7ecb5f2..00000000
Binary files a/src/ar/xsanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ar/xsmovie/res/web_hi_res_512.png b/src/ar/xsmovie/res/web_hi_res_512.png
deleted file mode 100644
index 4ac97393..00000000
Binary files a/src/ar/xsmovie/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/de/animeloads/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 8dc560dd..00000000
Binary files a/src/de/animeloads/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/de/animeloads/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 03d51624..00000000
Binary files a/src/de/animeloads/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/de/animeloads/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index f8feb6e5..00000000
Binary files a/src/de/animeloads/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/de/animeloads/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 3eaed0fb..00000000
Binary files a/src/de/animeloads/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/de/animeloads/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 1b560124..00000000
Binary files a/src/de/animeloads/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/de/animeloads/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index da274ed6..00000000
Binary files a/src/de/animeloads/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/de/animeloads/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 369383ac..00000000
Binary files a/src/de/animeloads/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/de/animeloads/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index ec6a2ff3..00000000
Binary files a/src/de/animeloads/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/de/animeloads/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index de26e98f..00000000
Binary files a/src/de/animeloads/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/animeloads/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/de/animeloads/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 050b46e2..00000000
Binary files a/src/de/animeloads/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/de/aniworld/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 8dc560dd..00000000
Binary files a/src/de/aniworld/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/de/aniworld/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 9ec03535..00000000
Binary files a/src/de/aniworld/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/de/aniworld/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index f8feb6e5..00000000
Binary files a/src/de/aniworld/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/de/aniworld/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 86df8abd..00000000
Binary files a/src/de/aniworld/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/de/aniworld/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 1b560124..00000000
Binary files a/src/de/aniworld/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/de/aniworld/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 3cb79f27..00000000
Binary files a/src/de/aniworld/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/de/aniworld/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 369383ac..00000000
Binary files a/src/de/aniworld/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/de/aniworld/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 366f1f42..00000000
Binary files a/src/de/aniworld/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/de/aniworld/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index de26e98f..00000000
Binary files a/src/de/aniworld/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/aniworld/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/de/aniworld/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index aaad5a4c..00000000
Binary files a/src/de/aniworld/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-hdpi/ic_launcher_background.png b/src/de/cineclix/res/mipmap-hdpi/ic_launcher_background.png
deleted file mode 100644
index 2d07511b..00000000
Binary files a/src/de/cineclix/res/mipmap-hdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-hdpi/ic_launcher_foreground.png b/src/de/cineclix/res/mipmap-hdpi/ic_launcher_foreground.png
deleted file mode 100644
index bde37154..00000000
Binary files a/src/de/cineclix/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-hdpi/ic_launcher_monochrome.png b/src/de/cineclix/res/mipmap-hdpi/ic_launcher_monochrome.png
deleted file mode 100644
index bde37154..00000000
Binary files a/src/de/cineclix/res/mipmap-hdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-mdpi/ic_launcher_background.png b/src/de/cineclix/res/mipmap-mdpi/ic_launcher_background.png
deleted file mode 100644
index 09b41700..00000000
Binary files a/src/de/cineclix/res/mipmap-mdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-mdpi/ic_launcher_foreground.png b/src/de/cineclix/res/mipmap-mdpi/ic_launcher_foreground.png
deleted file mode 100644
index 017b0039..00000000
Binary files a/src/de/cineclix/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-mdpi/ic_launcher_monochrome.png b/src/de/cineclix/res/mipmap-mdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 017b0039..00000000
Binary files a/src/de/cineclix/res/mipmap-mdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-xhdpi/ic_launcher_background.png b/src/de/cineclix/res/mipmap-xhdpi/ic_launcher_background.png
deleted file mode 100644
index ea00baaf..00000000
Binary files a/src/de/cineclix/res/mipmap-xhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-xhdpi/ic_launcher_foreground.png b/src/de/cineclix/res/mipmap-xhdpi/ic_launcher_foreground.png
deleted file mode 100644
index cc83ed87..00000000
Binary files a/src/de/cineclix/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-xhdpi/ic_launcher_monochrome.png b/src/de/cineclix/res/mipmap-xhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index cc83ed87..00000000
Binary files a/src/de/cineclix/res/mipmap-xhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-xxhdpi/ic_launcher_background.png b/src/de/cineclix/res/mipmap-xxhdpi/ic_launcher_background.png
deleted file mode 100644
index adcd3c25..00000000
Binary files a/src/de/cineclix/res/mipmap-xxhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-xxhdpi/ic_launcher_foreground.png b/src/de/cineclix/res/mipmap-xxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index bb12091e..00000000
Binary files a/src/de/cineclix/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/src/de/cineclix/res/mipmap-xxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index bb12091e..00000000
Binary files a/src/de/cineclix/res/mipmap-xxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-xxxhdpi/ic_launcher_background.png b/src/de/cineclix/res/mipmap-xxxhdpi/ic_launcher_background.png
deleted file mode 100644
index 08eb24e0..00000000
Binary files a/src/de/cineclix/res/mipmap-xxxhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/src/de/cineclix/res/mipmap-xxxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index e82ff961..00000000
Binary files a/src/de/cineclix/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/cineclix/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/src/de/cineclix/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index e82ff961..00000000
Binary files a/src/de/cineclix/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/de/cinemathek/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 8dc560dd..00000000
Binary files a/src/de/cinemathek/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/de/cinemathek/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index fe053c9f..00000000
Binary files a/src/de/cinemathek/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/de/cinemathek/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index f8feb6e5..00000000
Binary files a/src/de/cinemathek/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/de/cinemathek/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index bde9fb29..00000000
Binary files a/src/de/cinemathek/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/de/cinemathek/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 1b560124..00000000
Binary files a/src/de/cinemathek/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/de/cinemathek/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index a3747f4b..00000000
Binary files a/src/de/cinemathek/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/de/cinemathek/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 369383ac..00000000
Binary files a/src/de/cinemathek/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/de/cinemathek/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index e26743f1..00000000
Binary files a/src/de/cinemathek/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/de/cinemathek/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index de26e98f..00000000
Binary files a/src/de/cinemathek/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/cinemathek/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/de/cinemathek/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 6b899ae7..00000000
Binary files a/src/de/cinemathek/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/de/filmpalast/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 8dc560dd..00000000
Binary files a/src/de/filmpalast/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/de/filmpalast/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 4c5d378f..00000000
Binary files a/src/de/filmpalast/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/de/filmpalast/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index f8feb6e5..00000000
Binary files a/src/de/filmpalast/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/de/filmpalast/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 095e31f4..00000000
Binary files a/src/de/filmpalast/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/de/filmpalast/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 1b560124..00000000
Binary files a/src/de/filmpalast/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/de/filmpalast/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 8787a0af..00000000
Binary files a/src/de/filmpalast/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/de/filmpalast/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 369383ac..00000000
Binary files a/src/de/filmpalast/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/de/filmpalast/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 6da85ba0..00000000
Binary files a/src/de/filmpalast/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/de/filmpalast/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index de26e98f..00000000
Binary files a/src/de/filmpalast/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/filmpalast/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/de/filmpalast/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 36f49fd3..00000000
Binary files a/src/de/filmpalast/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/de/kinoking/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 8dc560dd..00000000
Binary files a/src/de/kinoking/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/de/kinoking/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 5acc8ad2..00000000
Binary files a/src/de/kinoking/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/de/kinoking/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index f8feb6e5..00000000
Binary files a/src/de/kinoking/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/de/kinoking/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 32a719b7..00000000
Binary files a/src/de/kinoking/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/de/kinoking/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 1b560124..00000000
Binary files a/src/de/kinoking/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/de/kinoking/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index c303b600..00000000
Binary files a/src/de/kinoking/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/de/kinoking/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 369383ac..00000000
Binary files a/src/de/kinoking/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/de/kinoking/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 53bb9806..00000000
Binary files a/src/de/kinoking/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/de/kinoking/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index de26e98f..00000000
Binary files a/src/de/kinoking/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/kinoking/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/de/kinoking/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 8e72d520..00000000
Binary files a/src/de/kinoking/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-hdpi/ic_launcher_background.png b/src/de/kool/res/mipmap-hdpi/ic_launcher_background.png
deleted file mode 100644
index 2d07511b..00000000
Binary files a/src/de/kool/res/mipmap-hdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-hdpi/ic_launcher_foreground.png b/src/de/kool/res/mipmap-hdpi/ic_launcher_foreground.png
deleted file mode 100644
index 259233d7..00000000
Binary files a/src/de/kool/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-hdpi/ic_launcher_monochrome.png b/src/de/kool/res/mipmap-hdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 259233d7..00000000
Binary files a/src/de/kool/res/mipmap-hdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-mdpi/ic_launcher_background.png b/src/de/kool/res/mipmap-mdpi/ic_launcher_background.png
deleted file mode 100644
index 09b41700..00000000
Binary files a/src/de/kool/res/mipmap-mdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-mdpi/ic_launcher_foreground.png b/src/de/kool/res/mipmap-mdpi/ic_launcher_foreground.png
deleted file mode 100644
index 360f5be2..00000000
Binary files a/src/de/kool/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-mdpi/ic_launcher_monochrome.png b/src/de/kool/res/mipmap-mdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 360f5be2..00000000
Binary files a/src/de/kool/res/mipmap-mdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-xhdpi/ic_launcher_background.png b/src/de/kool/res/mipmap-xhdpi/ic_launcher_background.png
deleted file mode 100644
index ea00baaf..00000000
Binary files a/src/de/kool/res/mipmap-xhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-xhdpi/ic_launcher_foreground.png b/src/de/kool/res/mipmap-xhdpi/ic_launcher_foreground.png
deleted file mode 100644
index de15043d..00000000
Binary files a/src/de/kool/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-xhdpi/ic_launcher_monochrome.png b/src/de/kool/res/mipmap-xhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index de15043d..00000000
Binary files a/src/de/kool/res/mipmap-xhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-xxhdpi/ic_launcher_background.png b/src/de/kool/res/mipmap-xxhdpi/ic_launcher_background.png
deleted file mode 100644
index adcd3c25..00000000
Binary files a/src/de/kool/res/mipmap-xxhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-xxhdpi/ic_launcher_foreground.png b/src/de/kool/res/mipmap-xxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 902dd5b9..00000000
Binary files a/src/de/kool/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/src/de/kool/res/mipmap-xxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 902dd5b9..00000000
Binary files a/src/de/kool/res/mipmap-xxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-xxxhdpi/ic_launcher_background.png b/src/de/kool/res/mipmap-xxxhdpi/ic_launcher_background.png
deleted file mode 100644
index 08eb24e0..00000000
Binary files a/src/de/kool/res/mipmap-xxxhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/src/de/kool/res/mipmap-xxxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 7046d799..00000000
Binary files a/src/de/kool/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/kool/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/src/de/kool/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 7046d799..00000000
Binary files a/src/de/kool/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-hdpi/ic_launcher_background.png b/src/de/movie2k/res/mipmap-hdpi/ic_launcher_background.png
deleted file mode 100644
index 2d07511b..00000000
Binary files a/src/de/movie2k/res/mipmap-hdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-hdpi/ic_launcher_foreground.png b/src/de/movie2k/res/mipmap-hdpi/ic_launcher_foreground.png
deleted file mode 100644
index cf49c890..00000000
Binary files a/src/de/movie2k/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-hdpi/ic_launcher_monochrome.png b/src/de/movie2k/res/mipmap-hdpi/ic_launcher_monochrome.png
deleted file mode 100644
index cf49c890..00000000
Binary files a/src/de/movie2k/res/mipmap-hdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-mdpi/ic_launcher_background.png b/src/de/movie2k/res/mipmap-mdpi/ic_launcher_background.png
deleted file mode 100644
index 09b41700..00000000
Binary files a/src/de/movie2k/res/mipmap-mdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-mdpi/ic_launcher_foreground.png b/src/de/movie2k/res/mipmap-mdpi/ic_launcher_foreground.png
deleted file mode 100644
index d4276e61..00000000
Binary files a/src/de/movie2k/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-mdpi/ic_launcher_monochrome.png b/src/de/movie2k/res/mipmap-mdpi/ic_launcher_monochrome.png
deleted file mode 100644
index d4276e61..00000000
Binary files a/src/de/movie2k/res/mipmap-mdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-xhdpi/ic_launcher_background.png b/src/de/movie2k/res/mipmap-xhdpi/ic_launcher_background.png
deleted file mode 100644
index ea00baaf..00000000
Binary files a/src/de/movie2k/res/mipmap-xhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-xhdpi/ic_launcher_foreground.png b/src/de/movie2k/res/mipmap-xhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 6474e13c..00000000
Binary files a/src/de/movie2k/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-xhdpi/ic_launcher_monochrome.png b/src/de/movie2k/res/mipmap-xhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 6474e13c..00000000
Binary files a/src/de/movie2k/res/mipmap-xhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-xxhdpi/ic_launcher_background.png b/src/de/movie2k/res/mipmap-xxhdpi/ic_launcher_background.png
deleted file mode 100644
index adcd3c25..00000000
Binary files a/src/de/movie2k/res/mipmap-xxhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-xxhdpi/ic_launcher_foreground.png b/src/de/movie2k/res/mipmap-xxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 3777ec91..00000000
Binary files a/src/de/movie2k/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/src/de/movie2k/res/mipmap-xxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 3777ec91..00000000
Binary files a/src/de/movie2k/res/mipmap-xxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-xxxhdpi/ic_launcher_background.png b/src/de/movie2k/res/mipmap-xxxhdpi/ic_launcher_background.png
deleted file mode 100644
index 08eb24e0..00000000
Binary files a/src/de/movie2k/res/mipmap-xxxhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/src/de/movie2k/res/mipmap-xxxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index bbeb49b6..00000000
Binary files a/src/de/movie2k/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/de/movie2k/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/src/de/movie2k/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index bbeb49b6..00000000
Binary files a/src/de/movie2k/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/de/movie4k/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 8dc560dd..00000000
Binary files a/src/de/movie4k/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/de/movie4k/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 55c43ca1..00000000
Binary files a/src/de/movie4k/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/de/movie4k/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index f8feb6e5..00000000
Binary files a/src/de/movie4k/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/de/movie4k/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 44113ef3..00000000
Binary files a/src/de/movie4k/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/de/movie4k/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 1b560124..00000000
Binary files a/src/de/movie4k/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/de/movie4k/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 61101bff..00000000
Binary files a/src/de/movie4k/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/de/movie4k/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 369383ac..00000000
Binary files a/src/de/movie4k/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/de/movie4k/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index ee622825..00000000
Binary files a/src/de/movie4k/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/de/movie4k/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index de26e98f..00000000
Binary files a/src/de/movie4k/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/movie4k/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/de/movie4k/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 3e20bba5..00000000
Binary files a/src/de/movie4k/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/de/serienstream/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 8c5bf007..00000000
Binary files a/src/de/serienstream/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/de/serienstream/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 8dd53660..00000000
Binary files a/src/de/serienstream/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/de/serienstream/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 11c72b22..00000000
Binary files a/src/de/serienstream/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/de/serienstream/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index f5c1856c..00000000
Binary files a/src/de/serienstream/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/de/serienstream/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 0f790191..00000000
Binary files a/src/de/serienstream/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/de/serienstream/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 25655d87..00000000
Binary files a/src/de/serienstream/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/de/serienstream/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index e598be57..00000000
Binary files a/src/de/serienstream/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/de/serienstream/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 50887149..00000000
Binary files a/src/de/serienstream/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/de/serienstream/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 9ad66cf9..00000000
Binary files a/src/de/serienstream/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/serienstream/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/de/serienstream/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 846822fa..00000000
Binary files a/src/de/serienstream/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/de/streamcloud/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 8c5bf007..00000000
Binary files a/src/de/streamcloud/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/de/streamcloud/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 68d25f33..00000000
Binary files a/src/de/streamcloud/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/de/streamcloud/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 11c72b22..00000000
Binary files a/src/de/streamcloud/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/de/streamcloud/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index e4635082..00000000
Binary files a/src/de/streamcloud/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/de/streamcloud/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 0f790191..00000000
Binary files a/src/de/streamcloud/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/de/streamcloud/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index c935b1e2..00000000
Binary files a/src/de/streamcloud/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/de/streamcloud/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index e598be57..00000000
Binary files a/src/de/streamcloud/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/de/streamcloud/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index c7e083ea..00000000
Binary files a/src/de/streamcloud/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/de/streamcloud/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 9ad66cf9..00000000
Binary files a/src/de/streamcloud/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/de/streamcloud/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/de/streamcloud/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index d0da73c8..00000000
Binary files a/src/de/streamcloud/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/en/allanime/res/web_hi_res_512.png b/src/en/allanime/res/web_hi_res_512.png
deleted file mode 100644
index 77e5e5ca..00000000
Binary files a/src/en/allanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/allanimechi/res/web_hi_res_512.png b/src/en/allanimechi/res/web_hi_res_512.png
deleted file mode 100644
index 337bc317..00000000
Binary files a/src/en/allanimechi/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/allmovies/build.gradle b/src/en/allmovies/build.gradle
deleted file mode 100644
index c13c2047..00000000
--- a/src/en/allmovies/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-ext {
-    extName = 'AllMovies'
-    extClass = '.AllMovies'
-    extVersionCode = 12
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/en/allmovies/res/mipmap-hdpi/ic_launcher.png b/src/en/allmovies/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 977ed7b6..00000000
Binary files a/src/en/allmovies/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/allmovies/res/mipmap-mdpi/ic_launcher.png b/src/en/allmovies/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 4b61dcd8..00000000
Binary files a/src/en/allmovies/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/allmovies/res/mipmap-xhdpi/ic_launcher.png b/src/en/allmovies/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index ff41c03e..00000000
Binary files a/src/en/allmovies/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/allmovies/res/mipmap-xxhdpi/ic_launcher.png b/src/en/allmovies/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 9a4107d1..00000000
Binary files a/src/en/allmovies/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/allmovies/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/allmovies/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index c2db7ba3..00000000
Binary files a/src/en/allmovies/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/allmovies/res/web_hi_res_512.png b/src/en/allmovies/res/web_hi_res_512.png
deleted file mode 100644
index 93044256..00000000
Binary files a/src/en/allmovies/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/allmovies/src/eu/kanade/tachiyomi/animeextension/en/allmovies/AllMovies.kt b/src/en/allmovies/src/eu/kanade/tachiyomi/animeextension/en/allmovies/AllMovies.kt
deleted file mode 100644
index ed496de7..00000000
--- a/src/en/allmovies/src/eu/kanade/tachiyomi/animeextension/en/allmovies/AllMovies.kt
+++ /dev/null
@@ -1,337 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.allmovies
-
-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.AnimeFilter
-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 okhttp3.Headers
-import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
-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 AllMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
-
-    override val name = "AllMoviesForYou"
-
-    override val baseUrl = "https://allmoviesforyou.net"
-
-    override val lang = "en"
-
-    override val supportsLatest = false
-
-    private val preferences: SharedPreferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    // Popular Anime
-
-    override fun popularAnimeSelector(): String = "article.TPost > a"
-
-    override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/series/page/$page")
-
-    override fun popularAnimeFromElement(element: Element): SAnime {
-        val anime = SAnime.create()
-        anime.setUrlWithoutDomain(element.attr("href"))
-        anime.title = element.select("h2.Title").text()
-        anime.thumbnail_url = "https:" + element.select("div.Image figure img").attr("data-src")
-
-        return anime
-    }
-
-    override fun popularAnimeNextPageSelector(): String = "div.nav-links a:last-child"
-
-    // Episodes
-
-    override fun episodeListSelector() = throw UnsupportedOperationException()
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val document = response.asJsoup()
-        val episodeList = mutableListOf<SEpisode>()
-        val seriesLink = document.select("link[rel=canonical]").attr("abs:href")
-        if (seriesLink.contains("/series/")) {
-            val seasonsHtml = client.newCall(
-                GET(
-                    seriesLink,
-                    headers = Headers.headersOf("Referer", document.location()),
-                ),
-            ).execute().asJsoup()
-            val seasonsElements = seasonsHtml.select("section.SeasonBx.AACrdn a")
-            seasonsElements.forEach {
-                val seasonEpList = parseEpisodesFromSeries(it)
-                episodeList.addAll(seasonEpList)
-            }
-        } else {
-            val episode = SEpisode.create()
-            episode.name = document.select("div.TPMvCn h1.Title").text()
-            episode.episode_number = 1F
-            episode.setUrlWithoutDomain(seriesLink)
-            episodeList.add(episode)
-        }
-        return episodeList.reversed()
-    }
-
-    override fun episodeFromElement(element: Element): SEpisode {
-        val episode = SEpisode.create()
-        episode.episode_number = element.select("td > span.Num").text().toFloat()
-        val seasonNum = element.ownerDocument()!!.select("div.Title span").text()
-        episode.name = "Season $seasonNum" + "x" + element.select("td span.Num").text() + " : " + element.select("td.MvTbTtl > a").text()
-        episode.setUrlWithoutDomain(element.select("td.MvTbPly > a.ClA").attr("abs:href"))
-        return episode
-    }
-
-    private fun parseEpisodesFromSeries(element: Element): List<SEpisode> {
-        val seasonId = element.attr("abs:href")
-        val episodesHtml = client.newCall(GET(seasonId)).execute().asJsoup()
-        val episodeElements = episodesHtml.select("tr.Viewed")
-        return episodeElements.map { episodeFromElement(it) }
-    }
-
-    // Video urls
-
-    override fun videoListRequest(episode: SEpisode): Request {
-        val document = client.newCall(GET(baseUrl + episode.url)).execute().asJsoup()
-        val iframe = document.select("iframe[src*=/?trembed]").attr("abs:src")
-        return GET(iframe)
-    }
-
-    override fun videoListParse(response: Response): List<Video> {
-        val document = response.asJsoup()
-        return videosFromElement(document)
-    }
-
-    override fun videoListSelector() = "iframe"
-
-    private fun videosFromElement(document: Document): List<Video> {
-        val videoList = mutableListOf<Video>()
-        val elements = document.select(videoListSelector())
-        for (element in elements) {
-            val url = element.attr("abs:src")
-            val location = element.ownerDocument()!!.location()
-            val videoHeaders = Headers.headersOf("Referer", location)
-            when {
-                url.contains("https://dood") -> {
-                    val newQuality = "Doodstream mirror"
-                    val video = Video(url, newQuality, doodUrlParse(url), headers = videoHeaders)
-                    videoList.add(video)
-                }
-                url.contains("streamhub") -> {
-                    val response = client.newCall(GET(url, videoHeaders)).execute().asJsoup()
-                    val script = response.selectFirst("script:containsData(m3u8)")!!
-                    val data = script.data()
-                    val masterUrl = masterExtractor(data)
-                    val masterPlaylist = client.newCall(GET(masterUrl)).execute().body.string()
-                    masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {
-                        val quality = it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p"
-                        val videoUrl = it.substringAfter("\n").substringBefore("\n")
-                        videoList.add(Video(videoUrl, quality, videoUrl))
-                    }
-                }
-            }
-        }
-        return videoList
-    }
-
-    private fun masterExtractor(code: String): String {
-        val stringsRegex = """(?<!\\)'.+?(?<!\\)'""".toRegex()
-        val strings = stringsRegex.findAll(code).map {
-            it.value
-        }.toList()
-        var p = strings[3]
-        val k = strings[4].split('|')
-
-        val numbersRegex = """(?<=,)\d+(?=,)""".toRegex()
-        val numbers = numbersRegex.findAll(code).map {
-            it.value.toInt()
-        }.toList()
-        val a = numbers[0]
-        var c = numbers[1] - 1
-
-        while (c >= 0) {
-            val replaceRegex = ("""\b""" + c.toString(a) + """\b""").toRegex()
-            p = p.replace(replaceRegex, k[c])
-            c--
-        }
-
-        val sourcesRegex = """(?<=sources':\[\{src:").+?(?=")""".toRegex()
-        return sourcesRegex.find(p)!!.value
-    }
-
-    private fun doodUrlParse(url: String): String? {
-        val response = client.newCall(GET(url.replace("/d/", "/e/"))).execute()
-        val content = response.body.string()
-        if (!content.contains("'/pass_md5/")) return null
-        val md5 = content.substringAfter("'/pass_md5/").substringBefore("',")
-        val token = md5.substringAfterLast("/")
-        val doodTld = url.substringAfter("https://dood.").substringBefore("/")
-        val randomString = getRandomString()
-        val expiry = System.currentTimeMillis()
-        val videoUrlStart = client.newCall(
-            GET(
-                "https://dood.$doodTld/pass_md5/$md5",
-                Headers.headersOf("referer", url),
-            ),
-        ).execute().body.string()
-        return "$videoUrlStart$randomString?token=$token&expiry=$expiry"
-    }
-
-    private fun getRandomString(length: Int = 10): String {
-        val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
-        return (1..length)
-            .map { allowedChars.random() }
-            .joinToString("")
-    }
-    override fun List<Video>.sort(): List<Video> {
-        val quality = preferences.getString("preferred_quality", null)
-        if (quality != null) {
-            val newList = mutableListOf<Video>()
-            var preferred = 0
-            for (video in this) {
-                if (video.quality.contains(quality)) {
-                    newList.add(preferred, video)
-                    preferred++
-                } else {
-                    newList.add(video)
-                }
-            }
-            return newList
-        }
-        return this
-    }
-
-    override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
-
-    override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
-
-    // search
-
-    override fun searchAnimeFromElement(element: Element): SAnime {
-        val anime = SAnime.create()
-        anime.setUrlWithoutDomain(element.select("article a").attr("href"))
-        anime.title = element.select("h2.Title").text()
-        anime.thumbnail_url = "https:" + element.select("div.Image figure img").attr("data-src")
-        return anime
-    }
-
-    override fun searchAnimeNextPageSelector(): String = "div.nav-links a:last-child"
-
-    override fun searchAnimeSelector(): String = "ul.MovieList li"
-
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
-        return if (query.isNotBlank()) {
-            GET("$baseUrl/page/$page?s=$query", headers)
-        } else {
-            val url = "$baseUrl/category/".toHttpUrlOrNull()!!.newBuilder()
-            filters.forEach { filter ->
-                when (filter) {
-                    is GenreFilter -> url.addPathSegment(filter.toUriPart())
-                    else -> {}
-                }
-            }
-            url.addPathSegment("page")
-            url.addPathSegment("$page")
-            GET(url.toString(), headers)
-        }
-    }
-
-    // Details
-
-    override fun animeDetailsParse(document: Document): SAnime {
-        val anime = SAnime.create()
-        anime.title = document.select("div.TPMvCn h1.Title").text()
-        anime.genre = document.select("p.Genre a").joinToString(", ") { it.text() }
-        anime.status = parseStatus(document.select("div.Info").text()) // span.Qlty
-        anime.author = document.select("p.Director span a").joinToString(", ") { it.text() }
-        anime.description = document.select("div.TPMvCn div.Description p:first-of-type").text()
-        return anime
-    }
-
-    private fun parseStatus(status: String?) = when {
-        status == null -> SAnime.UNKNOWN
-        status.contains("AIR", ignoreCase = true) -> SAnime.ONGOING
-        else -> SAnime.COMPLETED
-    }
-
-    // Latest
-
-    override fun latestUpdatesNextPageSelector(): String = throw UnsupportedOperationException()
-
-    override fun latestUpdatesFromElement(element: Element): SAnime = throw UnsupportedOperationException()
-
-    override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
-
-    override fun latestUpdatesSelector(): String = throw UnsupportedOperationException()
-
-    // Filters
-
-    override fun getFilterList() = AnimeFilterList(
-        AnimeFilter.Header("NOTE: Ignored if using text search!"),
-        AnimeFilter.Separator(),
-        GenreFilter(getGenreList()),
-    )
-
-    private class GenreFilter(vals: Array<Pair<String, String>>) : UriPartFilter("Genres", vals)
-
-    private fun getGenreList() = arrayOf(
-        Pair("Action & Adventure", "action-adventure"),
-        Pair("Adventure", "aventure"),
-        Pair("Animation", "animation"),
-        Pair("Comedy", "comedy"),
-        Pair("Crime", "crime"),
-        Pair("Disney", "disney"),
-        Pair("Drama", "drama"),
-        Pair("Family", "family"),
-        Pair("Fantasy", "fantasy"),
-        Pair("History", "fistory"),
-        Pair("Horror", "horror"),
-        Pair("Kids", "kids"),
-        Pair("Music", "music"),
-        Pair("Mystery", "mystery"),
-        Pair("Reality", "reality"),
-        Pair("Romance", "romance"),
-        Pair("Sci-Fi & Fantasy", "sci-fi-fantasy"),
-        Pair("Science Fiction", "science-fiction"),
-        Pair("Thriller", "thriller"),
-        Pair("War", "war"),
-        Pair("War & Politics", "war-politics"),
-        Pair("Western", "western"),
-    )
-
-    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
-    }
-
-    // settings
-
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        val videoQualityPref = ListPreference(screen.context).apply {
-            key = "preferred_quality"
-            title = "Preferred quality"
-            entries = arrayOf("1080p", "720p", "480p", "360p", "240p")
-            entryValues = arrayOf("1080", "720", "480", "360", "240")
-            setDefaultValue("1080")
-            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()
-            }
-        }
-        screen.addPreference(videoQualityPref)
-    }
-}
diff --git a/src/en/animension/build.gradle b/src/en/animension/build.gradle
deleted file mode 100644
index 22c792f4..00000000
--- a/src/en/animension/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-ext {
-    extName = 'Animension'
-    extClass = '.Animension'
-    extVersionCode = 21
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(':lib:dood-extractor'))
-}
\ No newline at end of file
diff --git a/src/en/animension/res/mipmap-hdpi/ic_launcher.png b/src/en/animension/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index bcc321bc..00000000
Binary files a/src/en/animension/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animension/res/mipmap-mdpi/ic_launcher.png b/src/en/animension/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 62a56a76..00000000
Binary files a/src/en/animension/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animension/res/mipmap-xhdpi/ic_launcher.png b/src/en/animension/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 786de183..00000000
Binary files a/src/en/animension/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animension/res/mipmap-xxhdpi/ic_launcher.png b/src/en/animension/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 2b1e9f73..00000000
Binary files a/src/en/animension/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animension/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/animension/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 6393c77d..00000000
Binary files a/src/en/animension/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animension/res/web_hi_res_512.png b/src/en/animension/res/web_hi_res_512.png
deleted file mode 100644
index eb22d5d6..00000000
Binary files a/src/en/animension/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/animension/src/eu/kanade/tachiyomi/animeextension/en/animension/Animension.kt b/src/en/animension/src/eu/kanade/tachiyomi/animeextension/en/animension/Animension.kt
deleted file mode 100644
index 3ada470f..00000000
--- a/src/en/animension/src/eu/kanade/tachiyomi/animeextension/en/animension/Animension.kt
+++ /dev/null
@@ -1,213 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.animension
-
-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.doodextractor.DoodExtractor
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.util.asJsoup
-import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.json.Json
-import kotlinx.serialization.json.JsonArray
-import kotlinx.serialization.json.JsonObject
-import kotlinx.serialization.json.float
-import kotlinx.serialization.json.jsonArray
-import kotlinx.serialization.json.jsonPrimitive
-import kotlinx.serialization.json.long
-import okhttp3.Request
-import okhttp3.Response
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-
-class Animension() : ConfigurableAnimeSource, AnimeHttpSource() {
-    override val lang = "en"
-
-    override val name = "Animension"
-
-    override val baseUrl = "https://animension.to/"
-
-    override val supportsLatest = true
-
-    private val apiUrl = "https://animension.to/public-api"
-
-    private val json = Json {
-        ignoreUnknownKeys = true
-    }
-
-    private val preferences: SharedPreferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    // Popular
-    override fun popularAnimeRequest(page: Int): Request =
-        GET("$apiUrl/search.php?dub=0&sort=popular-week&page=$page")
-
-    override fun popularAnimeParse(response: Response): AnimesPage {
-        val responseJson = json.decodeFromString<JsonArray>(response.body.string())
-        val animes = responseJson.map { anime ->
-            val data = anime.jsonArray
-
-            SAnime.create().apply {
-                title = data[0].jsonPrimitive.content
-                url = data[1].jsonPrimitive.content
-                thumbnail_url = data[2].jsonPrimitive.content
-            }
-        }
-        val hasNextPage = responseJson.size >= 25
-
-        return AnimesPage(animes, hasNextPage)
-    }
-
-    // Episode
-    override fun episodeListRequest(anime: SAnime): Request {
-        return GET("$apiUrl/episodes.php?id=${anime.url}", headers)
-    }
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val responseJson = json.decodeFromString<JsonArray>(response.body.string())
-        val episodes = responseJson.map { episode ->
-            val data = episode.jsonArray
-
-            SEpisode.create().apply {
-                name = "Episode ${data[2]}"
-                url = data[1].jsonPrimitive.content
-                episode_number = data[2].jsonPrimitive.float
-                date_upload = data[3].jsonPrimitive.long.toMilli()
-            }
-        }
-        return episodes
-    }
-
-    // Video urls
-    override fun videoListRequest(episode: SEpisode): Request =
-        GET("$apiUrl/episode.php?id=${episode.url}", headers)
-
-    override fun videoListParse(response: Response): List<Video> {
-        val responseJson = json.decodeFromString<JsonArray>(response.body.string())
-        val videos = json.decodeFromString<JsonObject>(responseJson[3].jsonPrimitive.content)
-        val videoList = mutableListOf<Video>()
-
-        for (key in videos.keys.toList()) {
-            val url = videos[key]!!.jsonPrimitive.content
-            when {
-                url.contains("dood") -> {
-                    val video = DoodExtractor(client).videoFromUrl(url)
-                    if (video != null) {
-                        videoList.add(video)
-                    }
-                }
-            }
-        }
-        return videoList
-    }
-
-    override fun List<Video>.sort(): List<Video> {
-        val quality = preferences.getString("preferred_quality", null)
-        if (quality != null) {
-            val newList = mutableListOf<Video>()
-            var preferred = 0
-            for (video in this) {
-                if (video.quality.contains(quality)) {
-                    newList.add(preferred, video)
-                    preferred++
-                } else {
-                    newList.add(video)
-                }
-            }
-            return newList
-        }
-        return this
-    }
-
-    override fun videoUrlParse(response: Response) = throw UnsupportedOperationException()
-
-    // Anime details
-    override fun animeDetailsParse(response: Response): SAnime {
-        val document = response.asJsoup()
-        val anime = SAnime.create()
-        anime.thumbnail_url = document.select("div.thumb img").attr("src")
-        anime.title = document.select("h1.entry-title").text()
-        anime.description = document.select("div.desc").text()
-        anime.genre = document.select("div.genxed span a").joinToString { it.text() }
-        anime.status = parseStatus(document.select("div.spe span:contains(Status)").text().substringAfter("Status: "))
-        return anime
-    }
-
-    // Latest
-    override fun latestUpdatesRequest(page: Int): Request =
-        GET("$apiUrl/index.php?page=$page&mode=sub")
-
-    override fun latestUpdatesParse(response: Response): AnimesPage {
-        val responseJson = json.decodeFromString<JsonArray>(response.body.string())
-        val animes = responseJson.map { anime ->
-            val data = anime.jsonArray
-            SAnime.create().apply {
-                title = data[0].jsonPrimitive.content
-                url = data[1].jsonPrimitive.content
-                thumbnail_url = data[4].jsonPrimitive.content
-            }
-        }
-        val hasNextPage = responseJson.size >= 25
-
-        return AnimesPage(animes, hasNextPage)
-    }
-
-    // Search
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request =
-        GET("$apiUrl/search.php?search_text=$query&page=$page", headers)
-
-    override fun searchAnimeParse(response: Response): AnimesPage {
-        val responseJson = json.decodeFromString<JsonArray>(response.body.string())
-        val animes = responseJson.map { anime ->
-            val data = anime.jsonArray
-
-            SAnime.create().apply {
-                title = data[0].jsonPrimitive.content
-                url = data[1].jsonPrimitive.content
-                thumbnail_url = data[2].jsonPrimitive.content
-            }
-        }
-        val hasNextPage = responseJson.size >= 25
-
-        return AnimesPage(animes, hasNextPage)
-    }
-
-    // Utilities
-    private fun parseStatus(statusString: String): Int {
-        return when (statusString) {
-            "Ongoing" -> SAnime.ONGOING
-            "Finished" -> SAnime.COMPLETED
-            else -> SAnime.UNKNOWN
-        }
-    }
-
-    private fun Long.toMilli(): Long = this * 1000
-
-    // Preferences
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        val videoQualityPref = ListPreference(screen.context).apply {
-            key = "preferred_quality"
-            title = "Preferred quality"
-            entries = arrayOf("1080p", "720p", "480p", "360p", "Doodstream")
-            entryValues = arrayOf("1080", "720", "480", "360", "Doodstream")
-            setDefaultValue("1080")
-            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()
-            }
-        }
-        screen.addPreference(videoQualityPref)
-    }
-}
diff --git a/src/en/animeowl/build.gradle b/src/en/animeowl/build.gradle
index a03357bc..e5849d46 100644
--- a/src/en/animeowl/build.gradle
+++ b/src/en/animeowl/build.gradle
@@ -1,7 +1,7 @@
 ext {
     extName = 'AnimeOwl'
     extClass = '.AnimeOwl'
-    extVersionCode = 21
+    extVersionCode = 22
 }
 
 apply from: "$rootDir/common.gradle"
@@ -9,4 +9,4 @@ apply from: "$rootDir/common.gradle"
 dependencies {
     implementation(project(":lib:synchrony"))
     implementation(project(":lib:playlist-utils"))
-}
\ No newline at end of file
+}
diff --git a/src/en/animeowl/res/web_hi_res_512.png b/src/en/animeowl/res/web_hi_res_512.png
deleted file mode 100644
index 8305be17..00000000
Binary files a/src/en/animeowl/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/animeowl/src/eu/kanade/tachiyomi/animeextension/en/animeowl/AnimeOwl.kt b/src/en/animeowl/src/eu/kanade/tachiyomi/animeextension/en/animeowl/AnimeOwl.kt
index dee4ddad..b53606e8 100644
--- a/src/en/animeowl/src/eu/kanade/tachiyomi/animeextension/en/animeowl/AnimeOwl.kt
+++ b/src/en/animeowl/src/eu/kanade/tachiyomi/animeextension/en/animeowl/AnimeOwl.kt
@@ -38,7 +38,7 @@ class AnimeOwl : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
 
     override val name = "AnimeOwl"
 
-    override val baseUrl = "https://animeowl.live"
+    override val baseUrl = "https://animeowl.me"
 
     override val lang = "en"
 
diff --git a/src/en/animepahe/res/web_hi_res_512.png b/src/en/animepahe/res/web_hi_res_512.png
deleted file mode 100644
index 84c0ab15..00000000
Binary files a/src/en/animepahe/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/animeparadise/res/web_hi_res_512.png b/src/en/animeparadise/res/web_hi_res_512.png
deleted file mode 100644
index 1927b8de..00000000
Binary files a/src/en/animeparadise/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/animesakura/README.md b/src/en/animesakura/README.md
deleted file mode 100644
index 4e7ea87d..00000000
--- a/src/en/animesakura/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# DISCLAIMER
-
-This extension requires you to log in through Google and relies heavily on scraping the website of Google Drive, which may be against their terms of service. Use at your own risk.
diff --git a/src/en/animesakura/build.gradle b/src/en/animesakura/build.gradle
deleted file mode 100644
index bb7f4465..00000000
--- a/src/en/animesakura/build.gradle
+++ /dev/null
@@ -1,12 +0,0 @@
-ext {
-    extName = 'Anime Sakura'
-    extClass = '.AnimeSakura'
-    extVersionCode = 2
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(':lib:googledrive-extractor'))
-    implementation(project(':lib:googledrive-episodes'))
-}
\ No newline at end of file
diff --git a/src/en/animesakura/res/mipmap-hdpi/ic_launcher.png b/src/en/animesakura/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 9e53b7a9..00000000
Binary files a/src/en/animesakura/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animesakura/res/mipmap-mdpi/ic_launcher.png b/src/en/animesakura/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index caa402f4..00000000
Binary files a/src/en/animesakura/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animesakura/res/mipmap-xhdpi/ic_launcher.png b/src/en/animesakura/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 5d3bad60..00000000
Binary files a/src/en/animesakura/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animesakura/res/mipmap-xxhdpi/ic_launcher.png b/src/en/animesakura/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 95caa927..00000000
Binary files a/src/en/animesakura/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animesakura/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/animesakura/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 31d56757..00000000
Binary files a/src/en/animesakura/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/animesakura/src/eu/kanade/tachiyomi/animeextension/en/animesakura/AnimeSakura.kt b/src/en/animesakura/src/eu/kanade/tachiyomi/animeextension/en/animesakura/AnimeSakura.kt
deleted file mode 100644
index 552532bc..00000000
--- a/src/en/animesakura/src/eu/kanade/tachiyomi/animeextension/en/animesakura/AnimeSakura.kt
+++ /dev/null
@@ -1,477 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.animesakura
-
-import android.app.Application
-import android.content.SharedPreferences
-import androidx.preference.EditTextPreference
-import androidx.preference.PreferenceScreen
-import androidx.preference.SwitchPreferenceCompat
-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.AnimeHttpSource
-import eu.kanade.tachiyomi.lib.googledriveepisodes.GoogleDriveEpisodes
-import eu.kanade.tachiyomi.lib.googledriveextractor.GoogleDriveExtractor
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.util.asJsoup
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-import okhttp3.FormBody
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.Request
-import okhttp3.Response
-import org.jsoup.Jsoup
-import org.jsoup.nodes.Element
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import uy.kohesive.injekt.injectLazy
-
-class AnimeSakura : ConfigurableAnimeSource, AnimeHttpSource() {
-
-    override val name = "Anime Sakura"
-
-    override val baseUrl = "https://animesakura.co"
-
-    override val lang = "en"
-
-    // Used for loading anime
-    private var infoQuery = ""
-    private var max = ""
-    private var latestPost = ""
-    private var layout = ""
-    private var settings = ""
-    private var currentReferer = ""
-
-    override val supportsLatest = true
-
-    private val json: Json by injectLazy()
-
-    private val preferences: SharedPreferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    // ============================== Popular ===============================
-
-    override fun popularAnimeRequest(page: Int): Request {
-        val formBody = FormBody.Builder().apply {
-            add("action", "tie_blocks_load_more")
-            add("block[order]", "views")
-            add("block[asc_or_desc]", "DESC")
-            add("block[id][]", "3")
-            add("block[number]", "24")
-            addExtra(page)
-        }.build()
-
-        val formHeaders = headersBuilder().apply {
-            add("Accept", "*/*")
-            add("Host", baseUrl.toHttpUrl().host)
-            add("Origin", baseUrl)
-            add("Referer", "$baseUrl/anime-series/")
-            add("X-Requested-With", "XMLHttpRequest")
-        }.build()
-
-        return POST("$baseUrl/wp-admin/admin-ajax.php", formHeaders, formBody)
-    }
-
-    override fun popularAnimeParse(response: Response): AnimesPage {
-        val body = response.body.string()
-        val rawParsed = json.decodeFromString<String>(body)
-        val parsed = json.decodeFromString<PostResponse>(rawParsed)
-        val document = Jsoup.parseBodyFragment(parsed.code)
-
-        val animeList = document.select("li.post-item")
-            .map(::popularAnimeFromElement)
-
-        return AnimesPage(animeList, !parsed.hide_next)
-    }
-
-    private fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
-        setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
-        thumbnail_url = element.selectFirst("img[src]")?.attr("src") ?: ""
-        title = element.selectFirst("h2.post-title")!!.text().substringBefore(" Episode")
-    }
-
-    // =============================== Latest ===============================
-
-    override fun latestUpdatesRequest(page: Int): Request {
-        val formBody = FormBody.Builder().apply {
-            add("action", "tie_blocks_load_more")
-            add("block[asc_or_desc]", "DESC")
-            add("block[id][]", "14")
-            add("block[number]", "10")
-            addExtra(page)
-        }.build()
-
-        val formHeaders = headersBuilder().apply {
-            add("Accept", "*/*")
-            add("Host", baseUrl.toHttpUrl().host)
-            add("Origin", baseUrl)
-            add("Referer", "$baseUrl/ongoing-anime/")
-            add("X-Requested-With", "XMLHttpRequest")
-        }.build()
-
-        return POST("$baseUrl/wp-admin/admin-ajax.php", formHeaders, formBody)
-    }
-
-    override fun latestUpdatesParse(response: Response): AnimesPage = popularAnimeParse(response)
-
-    // =============================== Search ===============================
-
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
-        val subPage = filters.filterIsInstance<SubPageFilter>().first().toUriPart()
-        val genreFilter = filters.filterIsInstance<GenreFilter>().first().toUriPart()
-
-        if (query.isEmpty() && subPage.isNotEmpty()) {
-            val formBody = FormBody.Builder().apply {
-                add("action", "tie_blocks_load_more")
-                add("block[asc_or_desc]", "DESC")
-                add("block[id][]", "35")
-                add("block[number]", "15")
-                addExtra(page)
-            }.build()
-            val formHeaders = headersBuilder().apply {
-                add("Accept", "*/*")
-                add("Host", baseUrl.toHttpUrl().host)
-                add("Origin", baseUrl)
-                add("Referer", "$baseUrl/anime-movies/")
-                add("X-Requested-With", "XMLHttpRequest")
-            }.build()
-            return POST("$baseUrl/wp-admin/admin-ajax.php", formHeaders, formBody)
-        }
-
-        return if (page == 1) {
-            infoQuery = ""
-            max = ""
-            latestPost = ""
-            layout = ""
-            settings = ""
-            currentReferer = ""
-
-            val docHeaders = headersBuilder().apply {
-                add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
-            }.build()
-
-            if (query.isNotEmpty()) {
-                val url = baseUrl.toHttpUrl().newBuilder().apply {
-                    addPathSegment("")
-                    addQueryParameter("s", query)
-                }.build()
-                currentReferer = url.toString()
-                GET(url, docHeaders)
-            } else if (genreFilter.isNotEmpty()) {
-                currentReferer = "$baseUrl/category/$genreFilter"
-                GET("$baseUrl/category/$genreFilter", docHeaders)
-            } else {
-                currentReferer = "$baseUrl/?s="
-                GET("$baseUrl/?s=", docHeaders)
-            }
-        } else {
-            val formBody = FormBody.Builder().apply {
-                add("action", "tie_archives_load_more")
-                add("query", infoQuery)
-                add("max", max)
-                add("page", page.toString())
-                add("latest_post", latestPost)
-                add("layout", layout)
-                add("settings", settings)
-            }.build()
-            val formHeaders = headersBuilder().apply {
-                add("Accept", "*/*")
-                add("Host", baseUrl.toHttpUrl().host)
-                add("Origin", baseUrl)
-                add("Referer", currentReferer)
-                add("X-Requested-With", "XMLHttpRequest")
-            }.build()
-            POST("$baseUrl/wp-admin/admin-ajax.php", formHeaders, formBody)
-        }
-    }
-
-    override fun searchAnimeParse(response: Response): AnimesPage {
-        return if (response.request.url.toString().contains("admin-ajax")) {
-            popularAnimeParse(response)
-        } else {
-            val document = response.asJsoup()
-            val animeList = document.select("ul#posts-container > li.post-item").map { element ->
-                SAnime.create().apply {
-                    setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href"))
-                    thumbnail_url = element.selectFirst("img")!!.imgAttr()
-                    title = element.selectFirst("h2.post-title")!!.text().substringBefore(" Episode")
-                }
-            }
-            val hasNextPage = document.selectFirst("div.pages-nav > a[data-text=load more]") != null
-            if (hasNextPage) {
-                val container = document.selectFirst("ul#posts-container")!!
-                val pagesNav = document.selectFirst("div.pages-nav > a")!!
-                layout = container.attr("data-layout")
-                infoQuery = pagesNav.attr("data-query")
-                max = pagesNav.attr("data-max")
-                latestPost = pagesNav.attr("data-latest")
-                settings = container.attr("data-settings")
-            }
-
-            AnimesPage(animeList, hasNextPage)
-        }
-    }
-
-    // ============================== Filters ===============================
-
-    override fun getFilterList(): AnimeFilterList = AnimeFilterList(
-        AnimeFilter.Header("Text search ignores filters"),
-        SubPageFilter(),
-        GenreFilter(),
-    )
-
-    private class SubPageFilter : UriPartFilter(
-        "Sub-page",
-        arrayOf(
-            Pair("<select>", ""),
-            Pair("Anime Movies", "anime-movies"),
-        ),
-    )
-
-    private class GenreFilter : UriPartFilter(
-        "Genres",
-        arrayOf(
-            Pair("<select>", ""),
-            Pair("Action", "action"),
-            Pair("Adventure", "adventure"),
-            Pair("Romance", "romance"),
-            Pair("Ecchi", "ecchi"),
-            Pair("School", "school"),
-            Pair("Harem", "harem"),
-            Pair("Sci-fi", "sci-fi"),
-            Pair("Comedy", "comedy"),
-            Pair("Drama", "drama"),
-            Pair("Mystery", "mystery"),
-            Pair("Military", "military"),
-            Pair("Fantasy", "fantasy"),
-            Pair("Isekai", "isekai"),
-            Pair("Psychological", "psychological"),
-            Pair("Shoujo", "shoujo"),
-            Pair("Slice of Life", "slice-of-life"),
-            Pair("Shounen", "shounen"),
-            Pair("Sports", "sports"),
-            Pair("Supernatural", "supernatural-2"),
-        ),
-    )
-
-    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
-    }
-
-    // =========================== Anime Details ============================
-
-    override fun animeDetailsParse(response: Response): SAnime {
-        val document = response.asJsoup()
-        val moreInfo = Jsoup.parseBodyFragment(
-            document.selectFirst("div.toggle-content li p")?.html()?.replace("<br>", "br2n") ?: "",
-        ).text().replace("br2n", "\n")
-        val realDesc = document.select("div.stream-item ~ p").joinToString("\n\n") { it.text() }
-
-        return SAnime.create().apply {
-            status = document.selectFirst("div.toggle-content > ul > li:contains(Status)")?.let {
-                parseStatus(it.text())
-            } ?: SAnime.UNKNOWN
-            description = realDesc + "\n\n$moreInfo"
-            genre = document.selectFirst("div.toggle-content > ul > li:contains(Genres)")?.let {
-                it.text().substringAfter("Genres").substringAfter("⋩ ").substringBefore(" ❀")
-            }
-            author = document.selectFirst("div.toggle-content > ul > li:contains(Studios)")?.let {
-                it.text().substringAfter("Studios").substringAfter("⋩ ").substringBefore("⁃")
-            }
-        }
-    }
-
-    // ============================== Episodes ==============================
-
-    private val googleDriveEpisodes by lazy { GoogleDriveEpisodes(client, headers) }
-    private val indexExtractor by lazy { DriveIndexExtractor(client, headers) }
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val document = response.asJsoup()
-        val episodeList = mutableListOf<SEpisode>()
-        val trimNames = preferences.trimEpisodeName
-        val blackListed = preferences.blKeywords
-
-        document.select("div.toggle:has(> div.toggle-content > a[href*=drive.google.com]),div.toggle:has(a.shortc-button[href*=drive.google.com])").distinctBy { t ->
-            getVideoPathsFromElement(t)
-        }.forEach { season ->
-            season.select("a[href*=drive.google.com]").distinctBy { it.text() }.forEach season@{
-                if (blackListed.any { t -> it.text().contains(t, true) }) return@season
-                val folderId = it.selectFirst("a[href*=drive.google.com]")!!.attr("abs:href").toHttpUrl().pathSegments[2]
-                episodeList.addAll(
-                    googleDriveEpisodes.getEpisodesFromFolder(folderId, "${getVideoPathsFromElement(season)} ${it.text()}", 2, trimNames),
-                )
-            }
-        }
-
-        document.select("div.wp-block-buttons > div.wp-block-button a[href*=drive.google.com]").distinctBy {
-            it.text()
-        }.forEach {
-            if (blackListed.any { t -> it.text().contains(t, true) }) return@forEach
-            val folderId = it.attr("abs:href").toHttpUrl().pathSegments[2]
-            episodeList.addAll(
-                googleDriveEpisodes.getEpisodesFromFolder(folderId, it.text(), 2, trimNames),
-            )
-        }
-
-        document.select("div.toggle:has(> div.toggle-content > a[href*=workers.dev]),div.toggle:has(a.shortc-button[href*=workers.dev])").distinctBy { t ->
-            getVideoPathsFromElement(t)
-        }.forEach { season ->
-            season.select("a[href*=workers.dev]").distinctBy { it.text() }.forEach season@{
-                if (blackListed.any { t -> it.text().contains(t, true) }) return@season
-                runCatching {
-                    episodeList.addAll(
-                        indexExtractor.getEpisodesFromIndex(it.attr("abs:href"), "${getVideoPathsFromElement(season)} ${it.text()}", trimNames),
-                    )
-                }
-            }
-        }
-
-        return episodeList.reversed()
-    }
-
-    private fun getVideoPathsFromElement(element: Element): String {
-        return element.selectFirst("h3")!!.text()
-            .substringBefore("480p").substringBefore("720p").substringBefore("1080p")
-            .replace("Download - ", "", true)
-            .replace("Download The Anime From Worker ?", "", true)
-            .replace("Download The Anime From Drive ", "", true)
-            .trim()
-    }
-
-    // ============================ Video Links =============================
-
-    private val googleDriveExtractor by lazy { GoogleDriveExtractor(client, headers) }
-
-    override suspend fun getVideoList(episode: SEpisode): List<Video> {
-        val httpUrl = episode.url.toHttpUrl()
-        val host = httpUrl.host
-        return if (host == "drive.google.com") {
-            val id = httpUrl.queryParameter("id")!!
-            googleDriveExtractor.videosFromUrl(id)
-        } else if (host.contains("workers.dev")) {
-            getIndexVideoUrl(episode.url)
-        } else {
-            throw Exception("Unsupported url: ${episode.url}")
-        }
-    }
-
-    // ============================= Utilities ==============================
-
-    private fun Element.imgAttr(): String = when {
-        hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
-        hasAttr("data-src") -> attr("abs:data-src")
-        else -> attr("abs:src")
-    }
-
-    private fun FormBody.Builder.addExtra(page: Int): FormBody.Builder {
-        add("block[pagi]", "show-more")
-        add("block[excerpt]", "true")
-        add("block[excerpt_length]", "15")
-        add("block[post_meta]", "true")
-        add("block[read_more]", "true")
-        add("block[breaking_effect]", "reveal")
-        add("block[sub_style]", "timeline")
-        add("block[style]", "timeline")
-        add("block[title_length]", "")
-        add("block[media_overlay]", "")
-        add("block[read_more_text]", "")
-        add("page", page.toString())
-        add("width", "single")
-        return this
-    }
-
-    private fun getIndexVideoUrl(url: String): List<Video> {
-        val doc = client.newCall(
-            GET("$url?a=view"),
-        ).execute().asJsoup()
-
-        val script = doc.selectFirst("script:containsData(videodomain)")?.data()
-            ?: doc.selectFirst("script:containsData(downloaddomain)")?.data()
-            ?: return listOf(Video(url, "Video", url))
-
-        if (script.contains("\"second_domain_for_dl\":false")) {
-            return listOf(Video(url, "Video", url))
-        }
-
-        val domainUrl = if (script.contains("videodomain", true)) {
-            script
-                .substringAfter("\"videodomain\":\"")
-                .substringBefore("\"")
-        } else {
-            script
-                .substringAfter("\"downloaddomain\":\"")
-                .substringBefore("\"")
-        }
-
-        val videoUrl = if (domainUrl.isBlank()) {
-            url
-        } else {
-            domainUrl + url.toHttpUrl().encodedPath
-        }
-
-        return listOf(Video(videoUrl, "Video", videoUrl))
-    }
-
-    @Serializable
-    data class PostResponse(
-        val hide_next: Boolean,
-        val code: String,
-    )
-
-    private fun parseStatus(statusString: String): Int {
-        return when (statusString) {
-            "Status: Currently Airing" -> SAnime.ONGOING
-            "Status: Finished Airing" -> SAnime.COMPLETED
-            else -> SAnime.UNKNOWN
-        }
-    }
-
-    companion object {
-        private const val TRIM_EPISODE_NAME_KEY = "trim_episode"
-        private const val TRIM_EPISODE_NAME_DEFAULT = true
-
-        private const val KEYWORD_BLACKLIST_KEY = "blacklist_words"
-        private const val KEYWORD_BLACKLIST_DEFAULT = "480p,720p"
-    }
-
-    private val SharedPreferences.trimEpisodeName
-        get() = getBoolean(TRIM_EPISODE_NAME_KEY, TRIM_EPISODE_NAME_DEFAULT)
-
-    private val SharedPreferences.blKeywords
-        get() = getString(KEYWORD_BLACKLIST_KEY, KEYWORD_BLACKLIST_DEFAULT)!!
-            .split(",")
-
-    // ============================== Settings ==============================
-
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        SwitchPreferenceCompat(screen.context).apply {
-            key = TRIM_EPISODE_NAME_KEY
-            title = "Trim info from episode name"
-            setDefaultValue(TRIM_EPISODE_NAME_DEFAULT)
-            setOnPreferenceChangeListener { _, newValue ->
-                preferences.edit().putBoolean(key, newValue as Boolean).commit()
-            }
-        }.also(screen::addPreference)
-
-        EditTextPreference(screen.context).apply {
-            key = KEYWORD_BLACKLIST_KEY
-            title = "Blacklist keywords"
-            summary = "Blacklist keywords, enter as a comma separated list"
-            setDefaultValue(KEYWORD_BLACKLIST_DEFAULT)
-
-            setOnPreferenceChangeListener { _, newValue ->
-                runCatching {
-                    val value = newValue as String
-                    preferences.edit().putString(key, value).commit()
-                }.getOrDefault(false)
-            }
-        }.also(screen::addPreference)
-    }
-}
diff --git a/src/en/animesakura/src/eu/kanade/tachiyomi/animeextension/en/animesakura/DriveIndexExtractor.kt b/src/en/animesakura/src/eu/kanade/tachiyomi/animeextension/en/animesakura/DriveIndexExtractor.kt
deleted file mode 100644
index 31f1835c..00000000
--- a/src/en/animesakura/src/eu/kanade/tachiyomi/animeextension/en/animesakura/DriveIndexExtractor.kt
+++ /dev/null
@@ -1,162 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.animesakura
-
-import android.util.Base64
-import eu.kanade.tachiyomi.animesource.model.SEpisode
-import eu.kanade.tachiyomi.network.POST
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-import okhttp3.Headers
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.MediaType.Companion.toMediaType
-import okhttp3.OkHttpClient
-import okhttp3.RequestBody.Companion.toRequestBody
-import uy.kohesive.injekt.injectLazy
-import java.net.URLEncoder
-
-class DriveIndexExtractor(private val client: OkHttpClient, private val headers: Headers) {
-
-    private val json: Json by injectLazy()
-
-    fun getEpisodesFromIndex(
-        indexUrl: String,
-        path: String,
-        trimName: Boolean,
-    ): List<SEpisode> {
-        val episodeList = mutableListOf<SEpisode>()
-
-        val basePathCounter = indexUrl.toHttpUrl().pathSegments.size
-
-        var counter = 1
-
-        fun traverseDirectory(url: String) {
-            var newToken: String? = ""
-            var newPageIndex = 0
-
-            while (newToken != null) {
-                val popHeaders = headers.newBuilder()
-                    .add("Accept", "*/*")
-                    .add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
-                    .add("Host", url.toHttpUrl().host)
-                    .add("Origin", "https://${url.toHttpUrl().host}")
-                    .add("Referer", URLEncoder.encode(url, "UTF-8"))
-                    .add("X-Requested-With", "XMLHttpRequest")
-                    .build()
-
-                val popBody = "password=&page_token=$newToken&page_index=$newPageIndex".toRequestBody("application/x-www-form-urlencoded".toMediaType())
-
-                val parsedBody = client.newCall(
-                    POST(url, body = popBody, headers = popHeaders),
-                ).execute().body.string().decrypt()
-                val parsed = json.decodeFromString<ResponseData>(parsedBody)
-
-                parsed.data.files.forEach { item ->
-                    if (item.mimeType.endsWith("folder")) {
-                        val newUrl = joinUrl(url, item.name).addSuffix("/")
-                        traverseDirectory(newUrl)
-                    }
-                    if (item.mimeType.startsWith("video/")) {
-                        val epUrl = joinUrl(url, item.name)
-                        val paths = epUrl.toHttpUrl().pathSegments
-
-                        // Get other info
-                        val season = if (paths.size == basePathCounter) {
-                            ""
-                        } else {
-                            paths[basePathCounter - 1]
-                        }
-                        val seasonInfoRegex = """(\([\s\w-]+\))(?: ?\[[\s\w-]+\])?${'$'}""".toRegex()
-                        val seasonInfo = if (seasonInfoRegex.containsMatchIn(season)) {
-                            "${seasonInfoRegex.find(season)!!.groups[1]!!.value} • "
-                        } else {
-                            ""
-                        }
-                        val extraInfo = if (paths.size > basePathCounter) {
-                            "/$path/" + paths.subList(basePathCounter - 1, paths.size - 1).joinToString("/") { it.trimInfo() }
-                        } else {
-                            "/$path"
-                        }
-                        val size = item.size?.toLongOrNull()?.let { formatFileSize(it) }
-
-                        episodeList.add(
-                            SEpisode.create().apply {
-                                name = if (trimName) item.name.trimInfo() else item.name
-                                this.url = epUrl
-                                scanlator = "${if (size == null) "" else "$size"} • $seasonInfo$extraInfo"
-                                date_upload = -1L
-                                episode_number = counter.toFloat()
-                            },
-                        )
-                        counter++
-                    }
-                }
-
-                newToken = parsed.nextPageToken
-                newPageIndex += 1
-            }
-        }
-
-        traverseDirectory(indexUrl)
-
-        return episodeList
-    }
-
-    @Serializable
-    data class ResponseData(
-        val nextPageToken: String? = null,
-        val data: DataObject,
-    ) {
-        @Serializable
-        data class DataObject(
-            val files: List<FileObject>,
-        ) {
-            @Serializable
-            data class FileObject(
-                val mimeType: String,
-                val id: String,
-                val name: String,
-                val modifiedTime: String? = null,
-                val size: String? = null,
-            )
-        }
-    }
-
-    private fun String.trimInfo(): String {
-        var newString = this.replaceFirst("""^\[\w+\] ?""".toRegex(), "")
-        val regex = """( ?\[[\s\w-]+\]| ?\([\s\w-]+\))(\.mkv|\.mp4|\.avi)?${'$'}""".toRegex()
-
-        while (regex.containsMatchIn(newString)) {
-            newString = regex.replace(newString) { matchResult ->
-                matchResult.groups[2]?.value ?: ""
-            }
-        }
-
-        return newString.trim()
-    }
-
-    private fun String.addSuffix(suffix: String): String {
-        return if (this.endsWith(suffix)) {
-            this
-        } else {
-            this.plus(suffix)
-        }
-    }
-
-    private fun String.decrypt(): String {
-        return Base64.decode(this.reversed().substring(24, this.length - 20), Base64.DEFAULT).toString(Charsets.UTF_8)
-    }
-
-    private fun joinUrl(path1: String, path2: String): String {
-        return path1.removeSuffix("/") + "/" + path2.removePrefix("/")
-    }
-
-    private fun formatFileSize(bytes: Long): String {
-        return when {
-            bytes >= 1073741824 -> "%.2f GB".format(bytes / 1073741824.0)
-            bytes >= 1048576 -> "%.2f MB".format(bytes / 1048576.0)
-            bytes >= 1024 -> "%.2f KB".format(bytes / 1024.0)
-            bytes > 1 -> "$bytes bytes"
-            bytes == 1L -> "$bytes byte"
-            else -> ""
-        }
-    }
-}
diff --git a/src/en/animetake/res/web_hi_res_512.png b/src/en/animetake/res/web_hi_res_512.png
deleted file mode 100644
index 5741c7d1..00000000
Binary files a/src/en/animetake/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/aniplay/ic_launcher-playstore.png b/src/en/aniplay/ic_launcher-playstore.png
deleted file mode 100644
index 7e70c67f..00000000
Binary files a/src/en/aniplay/ic_launcher-playstore.png and /dev/null differ
diff --git a/src/en/aniplay/res/web_hi_res_512.png b/src/en/aniplay/res/web_hi_res_512.png
deleted file mode 100644
index f56e465f..00000000
Binary files a/src/en/aniplay/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/asiaflix/res/web_hi_res_512.png b/src/en/asiaflix/res/web_hi_res_512.png
deleted file mode 100644
index a7cebae2..00000000
Binary files a/src/en/asiaflix/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/bestdubbedanime/res/web_hi_res_512.png b/src/en/bestdubbedanime/res/web_hi_res_512.png
deleted file mode 100644
index 6a31bcaa..00000000
Binary files a/src/en/bestdubbedanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/dopebox/res/play_store_512.png b/src/en/dopebox/res/play_store_512.png
deleted file mode 100644
index c67510b7..00000000
Binary files a/src/en/dopebox/res/play_store_512.png and /dev/null differ
diff --git a/src/en/dramacool/res/play_store_512.png b/src/en/dramacool/res/play_store_512.png
deleted file mode 100644
index 6b1a72a2..00000000
Binary files a/src/en/dramacool/res/play_store_512.png and /dev/null differ
diff --git a/src/en/edytjedhgmdhm/res/web_hi_res_512.png b/src/en/edytjedhgmdhm/res/web_hi_res_512.png
deleted file mode 100644
index 5c314e3c..00000000
Binary files a/src/en/edytjedhgmdhm/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/fmovies/res/web_hi_res_512.png b/src/en/fmovies/res/web_hi_res_512.png
deleted file mode 100644
index 116e1e1c..00000000
Binary files a/src/en/fmovies/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/gogoanime/res/web_hi_res_512.png b/src/en/gogoanime/res/web_hi_res_512.png
deleted file mode 100644
index a681ed1f..00000000
Binary files a/src/en/gogoanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/hahomoe/res/web_hi_res_512.png b/src/en/hahomoe/res/web_hi_res_512.png
deleted file mode 100644
index 0fe6a622..00000000
Binary files a/src/en/hahomoe/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/hanime/res/web_hi_res_500.png b/src/en/hanime/res/web_hi_res_500.png
deleted file mode 100644
index d80d06d9..00000000
Binary files a/src/en/hanime/res/web_hi_res_500.png and /dev/null differ
diff --git a/src/en/hentaimama/ic_launcher-playstore.png b/src/en/hentaimama/ic_launcher-playstore.png
deleted file mode 100644
index a3de5ca0..00000000
Binary files a/src/en/hentaimama/ic_launcher-playstore.png and /dev/null differ
diff --git a/src/en/hentaimama/res/web_hi_res_500.png b/src/en/hentaimama/res/web_hi_res_500.png
deleted file mode 100644
index a3de5ca0..00000000
Binary files a/src/en/hentaimama/res/web_hi_res_500.png and /dev/null differ
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/kaido/res/play_store_512.png b/src/en/kaido/res/play_store_512.png
deleted file mode 100644
index 4115b0c6..00000000
Binary files a/src/en/kaido/res/play_store_512.png and /dev/null differ
diff --git a/src/en/kawaiifu/res/web_hi_res_512.png b/src/en/kawaiifu/res/web_hi_res_512.png
deleted file mode 100644
index 40e21f8c..00000000
Binary files a/src/en/kawaiifu/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/kayoanime/res/web_hi_res_512.png b/src/en/kayoanime/res/web_hi_res_512.png
deleted file mode 100644
index 6931c263..00000000
Binary files a/src/en/kayoanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/kickassanime/res/web_hi_res_512.png b/src/en/kickassanime/res/web_hi_res_512.png
deleted file mode 100644
index 01378e9b..00000000
Binary files a/src/en/kickassanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/kimoitv/res/web_hi_res_512.png b/src/en/kimoitv/res/web_hi_res_512.png
deleted file mode 100644
index 9ee07beb..00000000
Binary files a/src/en/kimoitv/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/kissanime/res/web_hi_res_512.png b/src/en/kissanime/res/web_hi_res_512.png
deleted file mode 100644
index 582bb486..00000000
Binary files a/src/en/kissanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-hdpi/ic_launcher_background.png b/src/en/kisskh/res/mipmap-hdpi/ic_launcher_background.png
deleted file mode 100644
index ec6292a2..00000000
Binary files a/src/en/kisskh/res/mipmap-hdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-hdpi/ic_launcher_foreground.png b/src/en/kisskh/res/mipmap-hdpi/ic_launcher_foreground.png
deleted file mode 100644
index 071fd1db..00000000
Binary files a/src/en/kisskh/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-hdpi/ic_launcher_monochrome.png b/src/en/kisskh/res/mipmap-hdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 071fd1db..00000000
Binary files a/src/en/kisskh/res/mipmap-hdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-mdpi/ic_launcher_background.png b/src/en/kisskh/res/mipmap-mdpi/ic_launcher_background.png
deleted file mode 100644
index df2d7e08..00000000
Binary files a/src/en/kisskh/res/mipmap-mdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-mdpi/ic_launcher_foreground.png b/src/en/kisskh/res/mipmap-mdpi/ic_launcher_foreground.png
deleted file mode 100644
index 1050e1ca..00000000
Binary files a/src/en/kisskh/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-mdpi/ic_launcher_monochrome.png b/src/en/kisskh/res/mipmap-mdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 1050e1ca..00000000
Binary files a/src/en/kisskh/res/mipmap-mdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-xhdpi/ic_launcher_background.png b/src/en/kisskh/res/mipmap-xhdpi/ic_launcher_background.png
deleted file mode 100644
index 08c6199c..00000000
Binary files a/src/en/kisskh/res/mipmap-xhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-xhdpi/ic_launcher_foreground.png b/src/en/kisskh/res/mipmap-xhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 8593420b..00000000
Binary files a/src/en/kisskh/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-xhdpi/ic_launcher_monochrome.png b/src/en/kisskh/res/mipmap-xhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 8593420b..00000000
Binary files a/src/en/kisskh/res/mipmap-xhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-xxhdpi/ic_launcher_background.png b/src/en/kisskh/res/mipmap-xxhdpi/ic_launcher_background.png
deleted file mode 100644
index 8b158f91..00000000
Binary files a/src/en/kisskh/res/mipmap-xxhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-xxhdpi/ic_launcher_foreground.png b/src/en/kisskh/res/mipmap-xxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 1d3082f7..00000000
Binary files a/src/en/kisskh/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/src/en/kisskh/res/mipmap-xxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 1d3082f7..00000000
Binary files a/src/en/kisskh/res/mipmap-xxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-xxxhdpi/ic_launcher_background.png b/src/en/kisskh/res/mipmap-xxxhdpi/ic_launcher_background.png
deleted file mode 100644
index a1092411..00000000
Binary files a/src/en/kisskh/res/mipmap-xxxhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/src/en/kisskh/res/mipmap-xxxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 9def206c..00000000
Binary files a/src/en/kisskh/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/src/en/kisskh/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/src/en/kisskh/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
deleted file mode 100644
index 9def206c..00000000
Binary files a/src/en/kisskh/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and /dev/null differ
diff --git a/src/en/multimovies/build.gradle b/src/en/multimovies/build.gradle
deleted file mode 100644
index 47e15037..00000000
--- a/src/en/multimovies/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-ext {
-    extName = 'Multimovies'
-    extClass = '.Multimovies'
-    themePkg = 'dooplay'
-    baseUrl = 'https://multimovies.art'
-    overrideVersionCode = 20
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(':lib:streamlare-extractor'))
-    implementation(project(':lib:voe-extractor'))
-    implementation(project(':lib:dood-extractor'))
-    implementation(project(':lib:mixdrop-extractor'))
-    implementation(project(':lib:cryptoaes'))
-    implementation(libs.jsunpacker)
-}
\ No newline at end of file
diff --git a/src/en/multimovies/res/mipmap-hdpi/ic_launcher.png b/src/en/multimovies/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 1d118d11..00000000
Binary files a/src/en/multimovies/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/multimovies/res/mipmap-mdpi/ic_launcher.png b/src/en/multimovies/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 8b64905a..00000000
Binary files a/src/en/multimovies/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/multimovies/res/mipmap-xhdpi/ic_launcher.png b/src/en/multimovies/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index a4abae26..00000000
Binary files a/src/en/multimovies/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/multimovies/res/mipmap-xxhdpi/ic_launcher.png b/src/en/multimovies/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index fd112788..00000000
Binary files a/src/en/multimovies/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/multimovies/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/multimovies/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 23cbc002..00000000
Binary files a/src/en/multimovies/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/Multimovies.kt b/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/Multimovies.kt
deleted file mode 100644
index b018df07..00000000
--- a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/Multimovies.kt
+++ /dev/null
@@ -1,198 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.multimovies
-
-import android.app.Application
-import android.content.SharedPreferences
-import androidx.preference.ListPreference
-import androidx.preference.PreferenceScreen
-import eu.kanade.tachiyomi.animeextension.en.multimovies.extractors.AutoEmbedExtractor
-import eu.kanade.tachiyomi.animeextension.en.multimovies.extractors.MultimoviesCloudExtractor
-import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
-import eu.kanade.tachiyomi.animesource.model.SEpisode
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
-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.HttpUrl.Companion.toHttpUrl
-import okhttp3.Request
-import okhttp3.Response
-import org.jsoup.nodes.Element
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-
-class Multimovies : DooPlay(
-    "en",
-    "Multimovies",
-    "https://multimovies.art",
-) {
-    // TODO: Check frequency of url changes to potentially
-    // add back overridable baseurl preference
-    override val preferences: SharedPreferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    // ============================== Popular ===============================
-
-    override fun popularAnimeRequest(page: Int) = GET("$baseUrl/genre/anime-series/page/$page/")
-
-    override fun popularAnimeSelector() = latestUpdatesSelector()
-
-    override fun popularAnimeNextPageSelector() = latestUpdatesNextPageSelector()
-
-    // ============================== Episodes ==============================
-
-    override val seasonListSelector = "div#seasons > div:not(:contains(no episodes this season))"
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val doc = getRealAnimeDoc(response.asJsoup())
-        val seasonList = doc.select(seasonListSelector)
-        return if ("/movies/" in doc.location()) {
-            SEpisode.create().apply {
-                setUrlWithoutDomain(doc.location())
-                episode_number = 1F
-                name = episodeMovieText
-            }.let(::listOf)
-        } else if (seasonList.size < 1) {
-            throw Exception("The source provides ZERO episodes.")
-        } else {
-            seasonList.flatMap(::getSeasonEpisodes).reversed()
-        }
-    }
-
-    // ============================ Video Links =============================
-
-    override val prefQualityValues = arrayOf("1080p", "720p", "480p", "360p", "240p")
-    override val prefQualityEntries = arrayOf("1080", "720", "480", "360", "240")
-
-    override fun videoListParse(response: Response): List<Video> {
-        val document = response.asJsoup()
-        val players = document.select("ul#playeroptionsul li")
-        return players.flatMap(::getPlayerVideos)
-    }
-
-    private fun getPlayerVideos(player: Element): List<Video> {
-        if (player.attr("data-nume") == "trailer") return emptyList()
-        val url = getPlayerUrl(player)
-        return when {
-            url.contains(baseUrl.toHttpUrl().host, true) ->
-                MultimoviesCloudExtractor(client).videosFromUrl(url)
-            url.contains("autoembed.to") || url.contains("2embed.to") -> {
-                val newHeaders = headers.newBuilder()
-                    .set("Referer", url)
-                    .build()
-                AutoEmbedExtractor(client).videosFromUrl(url, headers = newHeaders)
-            }
-            url.contains("streamlare.com") -> StreamlareExtractor(client).videosFromUrl(url)
-            else -> 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("\\", "")
-                    .let { url ->
-                        when {
-                            url.lowercase().contains("iframe") -> {
-                                url.substringAfter("=\"")
-                                    .substringBefore("\" ")
-                            }
-                            else -> url
-                        }
-                    }
-            }
-    }
-
-    // =============================== Search ===============================
-
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
-        return if (query.isNotBlank()) {
-            GET("$baseUrl/page/$page/?s=$query", headers)
-        } else {
-            val filterList = if (filters.isEmpty()) getFilterList() else filters
-            val genreFilter = filterList.getFirst<GenreFilter>()
-            val streamingFilter = filterList.getFirst<StreamingFilter>()
-            val ficUniFilter = filterList.getFirst<FicUniFilter>()
-            val channelFilter = filterList.getFirst<ChannelFilter>()
-
-            when {
-                genreFilter.state != 0 -> GET("$baseUrl/genre/$genreFilter/page/$page", headers)
-                streamingFilter.state != 0 -> GET("$baseUrl/genre/$streamingFilter/page/$page", headers)
-                ficUniFilter.state != 0 -> GET("$baseUrl/genre/$ficUniFilter/page/$page", headers)
-                channelFilter.state != 0 -> GET("$baseUrl/genre/$channelFilter/page/$page", headers)
-                else -> popularAnimeRequest(page)
-            }
-        }
-    }
-
-    // ============================== Filters ===============================
-
-    override fun getFilterList() = getMultimoviesFilterList()
-    override val fetchGenres = false
-
-    // =============================== Latest ===============================
-
-    override fun latestUpdatesNextPageSelector() = "div.pagination > *:last-child:not(span):not(.current)"
-
-    // ============================== Settings ==============================
-
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        ListPreference(screen.context).apply {
-            key = PREF_SERVER_KEY
-            title = PREF_SERVER_TITLE
-            entries = PREF_SERVER_ENTRIES
-            entryValues = PREF_SERVER_VALUES
-            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)
-
-        super.setupPreferenceScreen(screen) // quality pref
-    }
-
-    // ============================= Utilities ==============================
-
-    private inline fun <reified R> AnimeFilterList.getFirst(): R {
-        return first { it is R } as R
-    }
-
-    companion object {
-        private const val PREF_SERVER_KEY = "preferred_server"
-        private const val PREF_SERVER_TITLE = "Preferred Server"
-        private const val PREF_SERVER_DEFAULT = "multimovies"
-        private val PREF_SERVER_ENTRIES = arrayOf(
-            "multimovies",
-            "2Embed Vidcloud",
-            "2Embed Voe",
-            "2Embed Streamlare",
-            "2Embed MixDrop",
-            "Gomo Dood",
-        )
-        private val PREF_SERVER_VALUES = arrayOf(
-            "multimovies",
-            "[2embed] server vidcloud",
-            "[2embed] server voe",
-            "[2embed] server streamlare",
-            "[2embed] server mixdrop",
-            "[gomostream] dood",
-        )
-    }
-}
diff --git a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/MultimoviesFilters.kt b/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/MultimoviesFilters.kt
deleted file mode 100644
index 94a9dec4..00000000
--- a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/MultimoviesFilters.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.multimovies
-
-import eu.kanade.tachiyomi.animesource.model.AnimeFilter
-import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
-
-internal fun getMultimoviesFilterList() = AnimeFilterList(
-    AnimeFilter.Header("NOTE: Ignored if using text search!"),
-    AnimeFilter.Separator(),
-    GenreFilter(getGenreList()),
-    StreamingFilter(getStreamingList()),
-    FicUniFilter(getFictionalUniverseList()),
-    ChannelFilter(getChannelList()),
-)
-
-internal class GenreFilter(vals: Array<Pair<String, String>>) : UriPartFilter("Genres", vals)
-internal class StreamingFilter(vals: Array<Pair<String, String>>) : UriPartFilter("Streaming service", vals)
-internal class FicUniFilter(vals: Array<Pair<String, String>>) : UriPartFilter("Fictional universe", vals)
-internal class ChannelFilter(vals: Array<Pair<String, String>>) : UriPartFilter("Channel", vals)
-
-internal fun getGenreList() = arrayOf(
-    Pair("<select>", ""),
-    Pair("Action", "action"),
-    Pair("Adventure", "adventure"),
-    Pair("Animation", "animation"),
-    Pair("Crime", "crime"),
-    Pair("Comedy", "comedy"),
-    Pair("Fantasy", "fantasy"),
-    Pair("Family", "family"),
-    Pair("Horror", "horror"),
-    Pair("Mystery", "mystery"),
-    Pair("Romance", "romance"),
-    Pair("Thriller", "thriller"),
-    Pair("Science Fiction", "science-fiction"),
-)
-
-internal fun getStreamingList() = arrayOf(
-    Pair("<select>", ""),
-    Pair("Amazon Prime", "amazon-prime"),
-    Pair("Disney Hotstar", "disney-hotstar"),
-    Pair("K-drama", "k-drama"),
-    Pair("Netflix", "netflix"),
-    Pair("Sony Liv", "sony-liv"),
-)
-
-internal fun getFictionalUniverseList() = arrayOf(
-    Pair("<select>", ""),
-    Pair("DC Universe", "dc-universe"),
-    Pair("Fast and Furious movies", "multimovies-com-fast-and-furious-movies"),
-    Pair("Harry Potter movies", "multimovies-com-harry-potter-movies"),
-    Pair("Marvel Collection", "marvel-collection"),
-    Pair("Mission Impossible", "mission-impossible-collection"),
-    Pair("Pirates of the Caribbean Collection", "pirates-of-the-caribbean-collection"),
-    Pair("Resident Evil", "resident-evil"),
-    Pair("Transformers Collection", "transformers-collection"),
-    Pair("Wrong Turn", "wrong-turn"),
-    Pair("X-Men Collection", "x-men-collection"),
-)
-
-internal fun getChannelList() = arrayOf(
-    Pair("<select>", ""),
-    Pair("Hindi Dub Anime", "anime-hindi"),
-    Pair("Anime Series", "anime-series"),
-    Pair("Anime Movies", "anime-movies"),
-    Pair("Cartoon Network", "cartoon-network"),
-    Pair("Disney Channel", "disney-channel"),
-    Pair("Disney XD", "disney-xd"),
-    Pair("Hungama", "hungama"),
-)
-
-open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
-    AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
-    override fun toString() = vals[state].second
-}
diff --git a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/extractors/AutoEmbedExtractor.kt b/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/extractors/AutoEmbedExtractor.kt
deleted file mode 100644
index e5ade955..00000000
--- a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/extractors/AutoEmbedExtractor.kt
+++ /dev/null
@@ -1,887 +0,0 @@
-/*
-Credit goes to @justfoolingaround and @Blatzar from https://github.com/recloudstream/cloudstream
-Code taken, and modified from https://github.com/recloudstream/cloudstream/blob/master/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
-
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
-  The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works.  By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.  We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors.  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-  To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights.  Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received.  You must make sure that they, too, receive
-or can get the source code.  And you must show them these terms so they
-know their rights.
-
-  Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
-  For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software.  For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
-  Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so.  This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software.  The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable.  Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products.  If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
-  Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary.  To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                       TERMS AND CONDITIONS
-
-  0. Definitions.
-
-  "This License" refers to version 3 of the GNU General Public License.
-
-  "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-  "The Program" refers to any copyrightable work licensed under this
-License.  Each licensee is addressed as "you".  "Licensees" and
-"recipients" may be individuals or organizations.
-
-  To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy.  The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-  A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-  To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy.  Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-  To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies.  Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-  An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License.  If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-  1. Source Code.
-
-  The "source code" for a work means the preferred form of the work
-for making modifications to it.  "Object code" means any non-source
-form of a work.
-
-  A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-  The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form.  A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-  The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities.  However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work.  For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-  The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-  The Corresponding Source for a work in source code form is that
-same work.
-
-  2. Basic Permissions.
-
-  All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met.  This License explicitly affirms your unlimited
-permission to run the unmodified Program.  The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work.  This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-  You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force.  You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright.  Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-  Conveying under any other circumstances is permitted solely under
-the conditions stated below.  Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-  No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-  When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-  4. Conveying Verbatim Copies.
-
-  You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-  You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-  5. Conveying Modified Source Versions.
-
-  You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified
-    it, and giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is
-    released under this License and any conditions added under section
-    7.  This requirement modifies the requirement in section 4 to
-    "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this
-    License to anyone who comes into possession of a copy.  This
-    License will therefore apply, along with any applicable section 7
-    additional terms, to the whole of the work, and all its parts,
-    regardless of how they are packaged.  This License gives no
-    permission to license the work in any other way, but it does not
-    invalidate such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display
-    Appropriate Legal Notices; however, if the Program has interactive
-    interfaces that do not display Appropriate Legal Notices, your
-    work need not make them do so.
-
-  A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit.  Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-  6. Conveying Non-Source Forms.
-
-  You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by the
-    Corresponding Source fixed on a durable physical medium
-    customarily used for software interchange.
-
-    b) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by a
-    written offer, valid for at least three years and valid for as
-    long as you offer spare parts or customer support for that product
-    model, to give anyone who possesses the object code either (1) a
-    copy of the Corresponding Source for all the software in the
-    product that is covered by this License, on a durable physical
-    medium customarily used for software interchange, for a price no
-    more than your reasonable cost of physically performing this
-    conveying of source, or (2) access to copy the
-    Corresponding Source from a network server at no charge.
-
-    c) Convey individual copies of the object code with a copy of the
-    written offer to provide the Corresponding Source.  This
-    alternative is allowed only occasionally and noncommercially, and
-    only if you received the object code with such an offer, in accord
-    with subsection 6b.
-
-    d) Convey the object code by offering access from a designated
-    place (gratis or for a charge), and offer equivalent access to the
-    Corresponding Source in the same way through the same place at no
-    further charge.  You need not require recipients to copy the
-    Corresponding Source along with the object code.  If the place to
-    copy the object code is a network server, the Corresponding Source
-    may be on a different server (operated by you or a third party)
-    that supports equivalent copying facilities, provided you maintain
-    clear directions next to the object code saying where to find the
-    Corresponding Source.  Regardless of what server hosts the
-    Corresponding Source, you remain obligated to ensure that it is
-    available for as long as needed to satisfy these requirements.
-
-    e) Convey the object code using peer-to-peer transmission, provided
-    you inform other peers where the object code and Corresponding
-    Source of the work are being offered to the general public at no
-    charge under subsection 6d.
-
-  A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-  A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling.  In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage.  For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product.  A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-  "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source.  The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-  If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information.  But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-  The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed.  Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-  Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-  7. Additional Terms.
-
-  "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law.  If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-  When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it.  (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.)  You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-  Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the
-    terms of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or
-    author attributions in that material or in the Appropriate Legal
-    Notices displayed by works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or
-    requiring that modified versions of such material be marked in
-    reasonable ways as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or
-    authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some
-    trade names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that
-    material by anyone who conveys the material (or modified versions of
-    it) with contractual assumptions of liability to the recipient, for
-    any liability that these contractual assumptions directly impose on
-    those licensors and authors.
-
-  All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10.  If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term.  If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-  If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-  Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-  8. Termination.
-
-  You may not propagate or modify a covered work except as expressly
-provided under this License.  Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-  However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-  Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-  Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-  9. Acceptance Not Required for Having Copies.
-
-  You are not required to accept this License in order to receive or
-run a copy of the Program.  Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance.  However,
-nothing other than this License grants you permission to propagate or
-modify any covered work.  These actions infringe copyright if you do
-not accept this License.  Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-  10. Automatic Licensing of Downstream Recipients.
-
-  Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License.  You are not responsible
-for enforcing compliance by third parties with this License.
-
-  An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations.  If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-  You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License.  For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-  11. Patents.
-
-  A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based.  The
-work thus licensed is called the contributor's "contributor version".
-
-  A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version.  For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-  Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-  In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement).  To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-  If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients.  "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-  If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-  A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License.  You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-  Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-  12. No Surrender of Others' Freedom.
-
-  If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-  13. Use with the GNU Affero General Public License.
-
-  Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work.  The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
-  14. Revised Versions of this License.
-
-  The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-  Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation.  If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-  If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-  Later license versions may give you additional or different
-permissions.  However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-  15. Disclaimer of Warranty.
-
-  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. Limitation of Liability.
-
-  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
-  17. Interpretation of Sections 15 and 16.
-
-  If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-  If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
-    <program>  Copyright (C) <year>  <name of author>
-    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
-  You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<https://www.gnu.org/licenses/>.
-
-  The GNU General Public License does not permit incorporating your program
-into proprietary programs.  If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.  But first, please read
-<https://www.gnu.org/licenses/why-not-lgpl.html>. */
-
-package eu.kanade.tachiyomi.animeextension.en.multimovies.extractors
-
-import android.util.Base64
-import app.cash.quickjs.QuickJs
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
-import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
-import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
-import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.json.Json
-import okhttp3.FormBody
-import okhttp3.Headers
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.OkHttpClient
-import uy.kohesive.injekt.injectLazy
-
-class AutoEmbedExtractor(private val client: OkHttpClient) {
-
-    private val json: Json by injectLazy()
-
-    fun videosFromUrl(url: String, headers: Headers): List<Video> {
-        val docHeaders = headers.newBuilder()
-            .add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
-            .add("Host", url.toHttpUrl().host)
-            .build()
-        val document = client.newCall(
-            GET(url, headers = docHeaders),
-        ).execute().asJsoup()
-
-        // First get all containers
-        val containerList = document.select("div > a[id*=server]").mapNotNull {
-            val slug = it.attr("href")
-            val newDocument = client.newCall(
-                GET("https://${url.toHttpUrl().host}$slug", headers = headers),
-            ).execute().asJsoup()
-
-            val container = newDocument.selectFirst("iframe[src]")!!.attr("src")
-                .substringAfter("replace(\"").substringBefore("\"")
-
-            when {
-                container.contains("2embed.to", true) -> Server(container, "2embed")
-                container.contains("gomostream", true) -> Server(container, "gomostream")
-                else -> null
-            }
-        }
-
-        // Get video servers from containers
-        val serverList = containerList.parallelCatchingFlatMapBlocking { container ->
-            when (container.name) {
-                "2embed" -> {
-                    getTwoEmbedServers(container.url, headers = headers)
-                }
-                "gomostream" -> {
-                    getGomoStreamServers(container.url, headers = headers)
-                }
-                else -> null
-            }.orEmpty()
-        }
-
-        val videoHeaders = headers.newBuilder()
-            .add("Referer", "https://www.2embed.to/")
-            .build()
-
-        return serverList.parallelCatchingFlatMapBlocking { server ->
-            val prefix = server.name
-            val videoUrl = server.url
-
-            when {
-                videoUrl.contains("streamlare") -> {
-                    StreamlareExtractor(client).videosFromUrl(videoUrl, prefix = prefix)
-                }
-                videoUrl.contains("mixdrop") -> {
-                    MixDropExtractor(client).videoFromUrl(videoUrl, prefix = prefix)
-                }
-                videoUrl.contains("https://voe") -> {
-                    VoeExtractor(client).videosFromUrl(videoUrl)
-                }
-                videoUrl.contains("rabbitstream") -> {
-                    RabbitStreamExtractor(client).videosFromUrl(videoUrl, headers = videoHeaders, prefix = prefix)
-                }
-                videoUrl.contains("https://dood") -> {
-                    DoodExtractor(client).videoFromUrl(videoUrl, server.name, false)
-                        ?.let(::listOf)
-                }
-                else -> null
-            }.orEmpty()
-        }
-    }
-
-    data class Server(
-        val url: String,
-        val name: String,
-    )
-
-    @Serializable
-    data class Stream(
-        val link: String,
-    )
-
-    private fun getGomoStreamServers(url: String, headers: Headers): List<Server> {
-        val response = client.newCall(GET(url, headers = headers)).execute()
-        val responseUrl = response.request.url
-        val document = response.asJsoup()
-        val script = document.selectFirst("script:containsData(_token)")?.data() ?: return emptyList()
-        val token = script.substringAfter("\"_token\": \"").substringBefore("\"")
-        val tokenCode = script.substringAfter("var tc = '").substringBefore("';")
-        val postUrl = script.substringAfter("\"POST\"").substringAfter("\"").substringBefore("\"")
-
-        // Function to get x-token
-        val functionName = script.substringAfter("'x-token': ").substringBefore("(")
-        val toExecute = "function $functionName${script.substringAfter("function $functionName")};$functionName(\"$tokenCode\")"
-
-        val xToken = QuickJs.create().use { it.evaluate(toExecute).toString() }
-
-        val postHeaders = Headers.headersOf(
-            "Accept", "*/*",
-            "Content-Type", "application/x-www-form-urlencoded; charset=UTF-8",
-            "Host", responseUrl.host,
-            "Origin", "https://${responseUrl.host}",
-            "Referer", responseUrl.toString(),
-            "User-Agent", headers["User-Agent"] ?: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0",
-            "X-Requested-With", "XMLHttpRequest",
-            "x-token", xToken,
-        )
-
-        val postBody = FormBody.Builder()
-            .add("tokenCode", tokenCode)
-            .add("_token", token)
-            .build()
-        val postResponse = client.newCall(
-            POST(postUrl, headers = postHeaders, body = postBody),
-        ).execute().body.string()
-        val list = json.decodeFromString<List<String>>(postResponse)
-
-        return list.filter { x -> x != "" }.map {
-            Server(it, "[gomostream] ${it.toHttpUrl().host} -")
-        }
-    }
-
-    private fun getTwoEmbedServers(url: String, headers: Headers): List<Server> {
-        val document = client.newCall(GET(url)).execute().asJsoup()
-
-        val captcha = document.selectFirst("script[src*=recaptcha/api.js]")!!
-            .attr("src")
-            .substringAfter("render=")
-
-        // Get video host urls
-        return document.select("div.dropdown-menu > a[data-id]").map {
-            val ajaxHeaders = Headers.headersOf(
-                "Accept", "*/*",
-                "Host", url.toHttpUrl().host,
-                "Referer", url,
-                "User-Agent", headers["User-Agent"]!!,
-                "X-Requested-With", "XMLHttpRequest",
-            )
-            val token = getCaptchaToken(url, captcha)
-
-            val streamUrl = client.newCall(
-                GET("https://www.2embed.to/ajax/embed/play?id=${it.attr("data-id")}&_token=$token", headers = ajaxHeaders),
-            ).execute().body.string()
-            val parsed = json.decodeFromString<Stream>(streamUrl)
-            Server(parsed.link, "[2embed] ${it.text()} - ")
-        }
-    }
-
-    // https://github.com/recloudstream/cloudstream/blob/7b47f93190fb2b106da44150c4431178eb3995dc/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt#L123
-    private fun getCaptchaToken(url: String, key: String): String? {
-        return runCatching {
-            val httpUrl = url.toHttpUrl()
-            val pureDomain = httpUrl.scheme + "://" + httpUrl.host + ":443"
-            val domain = Base64.encodeToString(pureDomain.encodeToByteArray(), Base64.DEFAULT)
-                .replace("\n", "")
-                .replace("=", ".")
-
-            val vToken = client.newCall(GET("https://www.google.com/recaptcha/api.js?render=$key"))
-                .execute()
-                .body.string()
-                .substringAfter("releases/")
-                .substringBefore("/")
-
-            client.newCall(
-                GET("https://www.google.com/recaptcha/api2/anchor?ar=1&hl=en&size=invisible&cb=cs3&k=$key&co=$domain&v=$vToken"),
-            ).execute()
-                .asJsoup()
-                .selectFirst("#recaptcha-token")
-                ?.attr("value")
-                ?.let { recapToken ->
-                    val body = FormBody.Builder()
-                        .add("v", vToken)
-                        .add("k", key)
-                        .add("c", recapToken)
-                        .add("co", domain)
-                        .add("sa", "")
-                        .add("reason", "q")
-                        .build()
-                    return client.newCall(
-                        POST("https://www.google.com/recaptcha/api2/reload?k=$key", body = body),
-                    ).execute().body.string().substringAfter("rresp\",\"").substringBefore("\"")
-                }
-        }.getOrNull()
-    }
-}
diff --git a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/extractors/MultimoviesCloudExtractor.kt b/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/extractors/MultimoviesCloudExtractor.kt
deleted file mode 100644
index 54604e49..00000000
--- a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/extractors/MultimoviesCloudExtractor.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.multimovies.extractors
-
-import dev.datlag.jsunpacker.JsUnpacker
-import eu.kanade.tachiyomi.animesource.model.Track
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.network.GET
-import okhttp3.OkHttpClient
-
-// Based on FilmPalast(de)'s StreamHideVidExtractor
-class MultimoviesCloudExtractor(private val client: OkHttpClient) {
-    // from nineanime / ask4movie FilemoonExtractor
-    private val subtitleRegex = Regex("""#EXT-X-MEDIA:TYPE=SUBTITLES.*?NAME="(.*?)".*?URI="(.*?)"""")
-
-    fun videosFromUrl(url: String): List<Video> {
-        val page = client.newCall(GET(url)).execute().body.string()
-        val unpacked = JsUnpacker.unpackAndCombine(page) ?: return emptyList()
-        val playlistUrl = unpacked.substringAfter("sources:")
-            .substringAfter("file:\"")
-            .substringBefore('"')
-
-        val playlistData = client.newCall(GET(playlistUrl)).execute().body.string()
-
-        val subs = subtitleRegex.findAll(playlistData).map {
-            val subUrl = fixUrl(it.groupValues[2], playlistUrl)
-            Track(subUrl, it.groupValues[1])
-        }.toList()
-
-        val separator = "#EXT-X-STREAM-INF"
-        return playlistData.substringAfter(separator).split(separator).map {
-            val resolution = it.substringAfter("RESOLUTION=")
-                .substringBefore("\n")
-                .substringAfter("x")
-                .substringBefore(",") + "p"
-
-            val urlPart = it.substringAfter("\n").substringBefore("\n")
-            val videoUrl = fixUrl(urlPart, playlistUrl)
-            Video(videoUrl, "[multimovies cloud] - $resolution", videoUrl, subtitleTracks = subs)
-        }
-    }
-
-    private fun fixUrl(urlPart: String, playlistUrl: String) =
-        when {
-            !urlPart.startsWith("https:") -> playlistUrl.substringBeforeLast("/") + "/$urlPart"
-            else -> urlPart
-        }
-}
diff --git a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/extractors/RabbitStreamExtractor.kt b/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/extractors/RabbitStreamExtractor.kt
deleted file mode 100644
index d501fda3..00000000
--- a/src/en/multimovies/src/eu/kanade/tachiyomi/animeextension/en/multimovies/extractors/RabbitStreamExtractor.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.multimovies.extractors
-
-import eu.kanade.tachiyomi.animesource.model.Track
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES.decrypt
-import eu.kanade.tachiyomi.network.GET
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-import okhttp3.Headers
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.OkHttpClient
-import uy.kohesive.injekt.injectLazy
-
-class RabbitStreamExtractor(private val client: OkHttpClient) {
-
-    // Prevent (automatic) caching the .JS file for different episodes, because it
-    // changes everytime, and a cached old .js will have a invalid AES password,
-    // invalidating the decryption algorithm.
-    // We cache it manually when initializing the class.
-    private val newClient = client.newBuilder()
-        .cache(null)
-        .build()
-
-    private val json: Json by injectLazy()
-
-    fun videosFromUrl(url: String, headers: Headers, prefix: String): List<Video> {
-        val httpUrl = url.toHttpUrl()
-        val host = httpUrl.host
-        val id = httpUrl.pathSegments.last()
-        val embed = httpUrl.pathSegments.first()
-
-        val newHeaders = headers.newBuilder()
-            .add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
-            .add("Host", host)
-            .build()
-
-        val jsonBody = client.newCall(
-            GET("https://rabbitstream.net/ajax/$embed/getSources?id=$id", headers = newHeaders),
-        ).execute().body.string()
-        val parsed = json.decodeFromString<Source>(jsonBody)
-
-        val key = newClient.newCall(
-            GET("https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt"),
-        ).execute().body.string()
-
-        val decrypted = decrypt(parsed.sources, key).ifEmpty { return emptyList() }
-        val subtitleList = parsed.tracks.map {
-            Track(it.file, it.label)
-        }
-
-        val files = json.decodeFromString<List<File>>(decrypted)
-        return files.flatMap { jsonFile ->
-            val videoHeaders = Headers.headersOf(
-                "Accept",
-                "*/*",
-                "Origin",
-                "https://$host",
-                "Referer",
-                "https://$host/",
-                "User-Agent",
-                headers["User-Agent"] ?: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0",
-            )
-
-            val masterPlaylist = client.newCall(
-                GET(jsonFile.file, headers = videoHeaders),
-            ).execute().body.string()
-
-            val separator = "#EXT-X-STREAM-INF"
-            masterPlaylist.substringAfter(separator).split(separator).map {
-                val quality = prefix + it.substringAfter("RESOLUTION=")
-                    .substringBefore("\n")
-                    .substringAfter("x")
-                    .substringBefore(",") + "p"
-                val videoUrl = it.substringAfter("\n").substringBefore("\n")
-                Video(videoUrl, quality, videoUrl, headers = videoHeaders, subtitleTracks = subtitleList)
-            }
-        }
-    }
-
-    @Serializable
-    data class Source(
-        val sources: String,
-        val tracks: List<Sub>,
-    ) {
-        @Serializable
-        data class Sub(
-            val file: String,
-            val label: String,
-        )
-    }
-
-    @Serializable
-    data class File(
-        val file: String,
-    )
-}
diff --git a/src/en/myanime/res/web_hi_res_512.png b/src/en/myanime/res/web_hi_res_512.png
deleted file mode 100644
index abee430c..00000000
Binary files a/src/en/myanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/noobsubs/res/web_hi_res_512.png b/src/en/noobsubs/res/web_hi_res_512.png
deleted file mode 100644
index 0e2027a2..00000000
Binary files a/src/en/noobsubs/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/putlocker/res/web_hi_res_512.png b/src/en/putlocker/res/web_hi_res_512.png
deleted file mode 100644
index c0b04c76..00000000
Binary files a/src/en/putlocker/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/ripcrabbyanime/README.md b/src/en/ripcrabbyanime/README.md
deleted file mode 100644
index 4e7ea87d..00000000
--- a/src/en/ripcrabbyanime/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# DISCLAIMER
-
-This extension requires you to log in through Google and relies heavily on scraping the website of Google Drive, which may be against their terms of service. Use at your own risk.
diff --git a/src/en/ripcrabbyanime/build.gradle b/src/en/ripcrabbyanime/build.gradle
deleted file mode 100644
index ba524dfe..00000000
--- a/src/en/ripcrabbyanime/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-ext {
-    extName = 'Ripcrabbyanime'
-    extClass = '.Ripcrabbyanime'
-    extVersionCode = 13
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(':lib:googledrive-extractor'))
-}
\ No newline at end of file
diff --git a/src/en/ripcrabbyanime/res/mipmap-hdpi/ic_launcher.png b/src/en/ripcrabbyanime/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index d422a9a4..00000000
Binary files a/src/en/ripcrabbyanime/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/ripcrabbyanime/res/mipmap-mdpi/ic_launcher.png b/src/en/ripcrabbyanime/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 92fb2609..00000000
Binary files a/src/en/ripcrabbyanime/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/ripcrabbyanime/res/mipmap-xhdpi/ic_launcher.png b/src/en/ripcrabbyanime/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index de3ed3e3..00000000
Binary files a/src/en/ripcrabbyanime/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/ripcrabbyanime/res/mipmap-xxhdpi/ic_launcher.png b/src/en/ripcrabbyanime/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 073c19b5..00000000
Binary files a/src/en/ripcrabbyanime/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/ripcrabbyanime/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/ripcrabbyanime/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 9e716cd6..00000000
Binary files a/src/en/ripcrabbyanime/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/ripcrabbyanime/res/web_hi_res_512.png b/src/en/ripcrabbyanime/res/web_hi_res_512.png
deleted file mode 100644
index ce14e2e2..00000000
Binary files a/src/en/ripcrabbyanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/ripcrabbyanime/src/eu/kanade/tachiyomi/animeextension/en/ripcrabbyanime/Ripcrabbyanime.kt b/src/en/ripcrabbyanime/src/eu/kanade/tachiyomi/animeextension/en/ripcrabbyanime/Ripcrabbyanime.kt
deleted file mode 100644
index 72b43551..00000000
--- a/src/en/ripcrabbyanime/src/eu/kanade/tachiyomi/animeextension/en/ripcrabbyanime/Ripcrabbyanime.kt
+++ /dev/null
@@ -1,432 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.ripcrabbyanime
-
-import android.app.Application
-import android.content.SharedPreferences
-import androidx.preference.PreferenceScreen
-import androidx.preference.SwitchPreferenceCompat
-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.googledriveextractor.GoogleDriveExtractor
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parseAs
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.MediaType.Companion.toMediaType
-import okhttp3.Request
-import okhttp3.RequestBody.Companion.toRequestBody
-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 uy.kohesive.injekt.injectLazy
-import java.security.MessageDigest
-
-class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
-
-    override val name = "Ripcrabbyanime"
-
-    override val id = 623659475482363776
-
-    // TODO: Check frequency of url changes to potentially
-    // add back overridable baseurl preference
-    override val baseUrl = "https://ripcrabbyanime.com"
-
-    override val lang = "en"
-
-    override val supportsLatest = false
-
-    private val json: Json by injectLazy()
-
-    private val preferences: SharedPreferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    // ============================== Popular ===============================
-
-    override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/ongoing-series/")
-
-    override fun popularAnimeSelector(): String = "section#movies-list div.movies-box"
-
-    override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
-        setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
-        thumbnail_url = element.selectFirst("img[src]")?.attr("src") ?: ""
-        title = element.selectFirst("a:matches(.)")!!.text().substringBefore(" | Episode").trimEnd()
-    }
-
-    override fun popularAnimeNextPageSelector(): String? = null
-
-    // =============================== 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()
-
-    // =============================== 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
-        val subPageFilter = filterList.find { it is SubPageFilter } as SubPageFilter
-
-        return when {
-            query.isNotBlank() -> {
-                val cleanQuery = query.replace(" ", "+")
-                GET("$baseUrl/search/?s=$cleanQuery")
-            }
-            genreFilter.state != 0 -> {
-                val url = "$baseUrl${genreFilter.toUriPart()}"
-                val body = "start=${(page - 1) * 40}&limit=40".toRequestBody("application/x-www-form-urlencoded".toMediaType())
-                POST(url, body = body)
-            }
-            subPageFilter.state != 0 -> {
-                val url = "$baseUrl${subPageFilter.toUriPart()}"
-                val body = "start=${(page - 1) * 40}&limit=40".toRequestBody("application/x-www-form-urlencoded".toMediaType())
-                POST(url, body = body)
-            }
-            else -> popularAnimeRequest(page)
-        }
-    }
-
-    override fun searchAnimeParse(response: Response): AnimesPage {
-        return if (response.request.url.encodedPath.startsWith("/search/")) {
-            popularAnimeParse(response)
-        } else {
-            val animeList = response.asJsoup()
-                .select(searchAnimeSelector())
-                .map(::popularAnimeFromElement)
-
-            return AnimesPage(animeList, animeList.size == 40)
-        }
-    }
-
-    override fun searchAnimeSelector(): String = "div#infinite-list"
-
-    override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
-
-    override fun searchAnimeNextPageSelector(): String? = popularAnimeNextPageSelector()
-
-    // ============================== Filters ===============================
-
-    override fun getFilterList(): AnimeFilterList = AnimeFilterList(
-        AnimeFilter.Header("Text search ignores filters"),
-        SubPageFilter(),
-        GenreFilter(),
-    )
-
-    private class SubPageFilter : UriPartFilter(
-        "Sub-page",
-        arrayOf(
-            Pair("<select>", ""),
-            Pair("Anime Series", "/Get/get-animeserise/"),
-            Pair("Anime Movie", "/Get/get-movies/"),
-        ),
-    )
-
-    private class GenreFilter : UriPartFilter(
-        "Genres",
-        arrayOf(
-            Pair("<select>", ""),
-            Pair("Action", "/Get/get-action/"),
-            Pair("Adventure", "/Get/get-adventure/"),
-            Pair("Comedy", "/Get/get-comedy/"),
-            Pair("Demons", "/Get/get-demons/"),
-            Pair("Drama", "/Get/get-drama/"),
-            Pair("Ecchi", "/Get/get-ecchi/"),
-            Pair("Fantasy", "/Get/get-fantasy/"),
-            Pair("Harem", "/Get/get-harem/"),
-            Pair("Horror", "/Get/get-horror/"),
-            Pair("Mecha", "/Get/get-mecha/"),
-            Pair("Magic", "/Get/get-magic/"),
-            Pair("Romance", "/Get/get-romance/"),
-            Pair("School", "/Get/get-school/"),
-            Pair("Sci-Fi", "/Get/get-sci-fi/"),
-            Pair("Shounen", "/Get/get-shounen/"),
-            Pair("Slice of Life", "/Get/get-slice-of-life/"),
-            Pair("Sports", "/Get/get-sports/"),
-            Pair("Super Power", "/Get/get-superpower/"),
-            Pair("Supernatural", "/Get/get-supernatural/"),
-        ),
-    )
-
-    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
-    }
-
-    // =========================== Anime Details ============================
-
-    override fun animeDetailsParse(document: Document): SAnime {
-        val moreInfo = document.select("div.summery:not(:has(h2:contains(Summary))) ul li").joinToString("\n") { it.ownText().trim() }
-        val realDesc = document.selectFirst("div.summery:has(h2:contains(Summary)) ul")?.let { "${it.text()}\n\n" } ?: ""
-
-        return SAnime.create().apply {
-            status = document.selectFirst("div.summery:not(:has(h2:contains(Summary))) ul li:contains(Status)")?.let {
-                parseStatus(it.text().substringAfter("Status: "))
-            } ?: SAnime.UNKNOWN
-            description = realDesc + "\n\n$moreInfo"
-            genre = document.selectFirst("div.summery:not(:has(h2:contains(Summary))) ul li:contains(Genres)")?.let {
-                it.text().substringAfter("Genres: ")
-            }
-            author = document.selectFirst("div.summery:not(:has(h2:contains(Summary))) ul li:contains(Studios)")?.let {
-                it.text().substringAfter("Studios: ").substringAfter("Studios ")
-            }
-        }
-    }
-
-    // ============================== Episodes ==============================
-
-    // Lots of code borrowed from https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/extractor/googledrive.py under the `GoogleDriveFolderIE` class
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val document = response.asJsoup()
-        val episodeList = mutableListOf<SEpisode>()
-
-        fun traverseFolder(url: String, path: String, recursionDepth: Int = 0) {
-            if (recursionDepth == MAX_RECURSION_DEPTH) return
-
-            val folderId = url.substringAfter("/folders/")
-            val driveHeaders = headers.newBuilder()
-                .add("Accept", "*/*")
-                .add("Connection", "keep-alive")
-                .add("Cookie", getCookie("https://drive.google.com"))
-                .add("Host", "drive.google.com")
-                .build()
-
-            val driveDocument = client.newCall(
-                GET(url, headers = driveHeaders),
-            ).execute().asJsoup()
-
-            if (driveDocument.selectFirst("title:contains(Error 404 \\(Not found\\))") != null) return
-
-            val keyScript = driveDocument.select("script").first { script ->
-                KEY_REGEX.find(script.data()) != null
-            }.data()
-            val key = KEY_REGEX.find(keyScript)?.groupValues?.get(1) ?: ""
-
-            val versionScript = driveDocument.select("script").first { script ->
-                KEY_REGEX.find(script.data()) != null
-            }.data()
-            val driveVersion = VERSION_REGEX.find(versionScript)?.groupValues?.get(1) ?: ""
-            val sapisid = client.cookieJar.loadForRequest("https://drive.google.com".toHttpUrl()).firstOrNull {
-                it.name == "SAPISID" || it.name == "__Secure-3PAPISID"
-            }?.value ?: ""
-
-            var pageToken: String? = ""
-            while (pageToken != null) {
-                val requestUrl = "/drive/v2internal/files?openDrive=false&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2ChasVisitorPermissions%2CcontainsUnsubscribedChildren%2CmodifiedByMeDate%2ClastViewedByMeDate%2CalternateLink%2CfileSize%2Cowners(kind%2CpermissionId%2CemailAddressFromAccount%2Cdomain%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CcustomerId%2CancestorHasAugmentedPermissions%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2CabuseIsAppealable%2CabuseNoticeReason%2Cshared%2CaccessRequestsCount%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2Csubscribed%2CfolderColor%2ChasChildFolders%2CfileExtension%2CprimarySyncParentId%2CsharingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CflaggedForAbuse%2CfolderFeatures%2Cspaces%2CsourceAppId%2Crecency%2CrecencyReason%2Cversion%2CactionItems%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CprimaryDomainName%2CorganizationDisplayName%2CpassivelySubscribed%2CtrashingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CtrashedDate%2Cparents(id)%2Ccapabilities(canMoveItemIntoTeamDrive%2CcanUntrash%2CcanMoveItemWithinTeamDrive%2CcanMoveItemOutOfTeamDrive%2CcanDeleteChildren%2CcanTrashChildren%2CcanRequestApproval%2CcanReadCategoryMetadata%2CcanEditCategoryMetadata%2CcanAddMyDriveParent%2CcanRemoveMyDriveParent%2CcanShareChildFiles%2CcanShareChildFolders%2CcanRead%2CcanMoveItemWithinDrive%2CcanMoveChildrenWithinDrive%2CcanAddFolderFromAnotherDrive%2CcanChangeSecurityUpdateEnabled%2CcanBlockOwner%2CcanReportSpamOrAbuse%2CcanCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2CcontentRestrictions(readOnly)%2CapprovalMetadata(approvalVersion%2CapprovalSummaries%2ChasIncomingApproval)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus%2CtargetFile%2CcanRequestAccessToTarget)%2CspamMetadata(markedAsSpamDate%2CinSpamView)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$pageToken&maxResults=100&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
-                val body = """--$BOUNDARY
-                    |content-type: application/http
-                    |content-transfer-encoding: binary
-                    |
-                    |GET $requestUrl
-                    |X-Goog-Drive-Client-Version: $driveVersion
-                    |authorization: ${generateSapisidhashHeader(sapisid)}
-                    |x-goog-authuser: 0
-                    |
-                    |--$BOUNDARY--""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
-
-                val postUrl = buildString {
-                    append("https://clients6.google.com/batch/drive/v2internal")
-                    append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
-                    append("&key=$key")
-                }
-
-                val postHeaders = headers.newBuilder()
-                    .add("Content-Type", "text/plain; charset=UTF-8")
-                    .add("Origin", "https://drive.google.com")
-                    .add("Cookie", getCookie("https://drive.google.com"))
-                    .build()
-
-                val response = client.newCall(
-                    POST(postUrl, body = body, headers = postHeaders),
-                ).execute()
-
-                val parsed = response.parseAs<PostResponse> {
-                    JSON_REGEX.find(it)!!.groupValues[1]
-                }
-
-                if (parsed.items == null) throw Exception("Failed to load items, please log in to google drive through webview")
-                parsed.items.forEachIndexed { index, it ->
-                    if (it.mimeType.startsWith("video")) {
-                        val size = it.fileSize?.toLongOrNull()?.let { formatBytes(it) }
-
-                        episodeList.add(
-                            SEpisode.create().apply {
-                                name = if (preferences.trimEpisodeName) it.title.trimInfo() else it.title
-                                this.url = "https://drive.google.com/uc?id=${it.id}"
-                                episode_number = ITEM_NUMBER_REGEX.find(it.title.trimInfo())?.groupValues?.get(1)?.toFloatOrNull() ?: index.toFloat()
-                                date_upload = -1L
-                                scanlator = "$size • /$path"
-                            },
-                        )
-                    }
-                    if (it.mimeType.endsWith(".folder")) {
-                        traverseFolder(
-                            "https://drive.google.com/drive/folders/${it.id}",
-                            "$path/${it.title}",
-                            recursionDepth + 1,
-                        )
-                    }
-                }
-
-                pageToken = parsed.nextPageToken
-            }
-        }
-
-        document.select("div.tokha > div > a[href]").distinctBy { t ->
-            t.text()
-        }.filter { t -> t.attr("href").isNotEmpty() }.forEach {
-            val url = it.attr("abs:href").toHttpUrl()
-            val noRedirectClient = client.newBuilder().followRedirects(false).build()
-
-            if (url.host.contains("drive.google.com")) {
-                val id = Regex("[\\w-]{28,}").find(
-                    url.toString().substringBeforeLast("?usp=shar"),
-                )!!.groupValues[0]
-
-                traverseFolder("https://drive.google.com/drive/folders/$id", it.text())
-            }
-            if (url.host.contains("tinyurl")) {
-                val redirected = noRedirectClient.newCall(GET(url.toString())).execute()
-                redirected.headers["location"]?.let { location ->
-                    if (location.toHttpUrl().host.contains("drive.google.com")) {
-                        val id = Regex("[\\w-]{28,}").find(
-                            location.substringBeforeLast("?usp=shar"),
-                        )!!.groupValues[0]
-
-                        traverseFolder("https://drive.google.com/drive/folders/$id", it.text())
-                    }
-                }
-            }
-        }
-
-        return episodeList.reversed()
-    }
-
-    override fun episodeListSelector(): String = throw UnsupportedOperationException()
-
-    override fun episodeFromElement(element: Element): SEpisode = throw UnsupportedOperationException()
-
-    // ============================ Video Links =============================
-
-    override suspend fun getVideoList(episode: SEpisode): List<Video> {
-        val videoList = GoogleDriveExtractor(client, headers).videosFromUrl(episode.url.substringAfter("?id="))
-        return videoList
-    }
-
-    override fun videoListSelector(): String = throw UnsupportedOperationException()
-
-    override fun videoFromElement(element: Element): Video = throw UnsupportedOperationException()
-
-    override fun videoUrlParse(document: Document): String = throw UnsupportedOperationException()
-
-    // ============================= Utilities ==============================
-
-    // https://github.com/yt-dlp/yt-dlp/blob/8f0be90ecb3b8d862397177bb226f17b245ef933/yt_dlp/extractor/youtube.py#L573
-    private fun generateSapisidhashHeader(SAPISID: String, origin: String = "https://drive.google.com"): String {
-        val timeNow = System.currentTimeMillis() / 1000
-        // SAPISIDHASH algorithm from https://stackoverflow.com/a/32065323
-        val sapisidhash = MessageDigest
-            .getInstance("SHA-1")
-            .digest("$timeNow $SAPISID $origin".toByteArray())
-            .joinToString("") { "%02x".format(it) }
-        return "SAPISIDHASH ${timeNow}_$sapisidhash"
-    }
-
-    @Serializable
-    data class PostResponse(
-        val nextPageToken: String? = null,
-        val items: List<ResponseItem>? = null,
-    ) {
-        @Serializable
-        data class ResponseItem(
-            val id: String,
-            val title: String,
-            val mimeType: String,
-            val fileSize: String? = null,
-        )
-    }
-
-    private fun String.trimInfo(): String {
-        var newString = this.replaceFirst("""^\[\w+\] """.toRegex(), "")
-        val regex = """( ?\[[\s\w-]+\]| ?\([\s\w-]+\))(\.mkv|\.mp4|\.avi)?${'$'}""".toRegex()
-
-        while (regex.containsMatchIn(newString)) {
-            newString = regex.replace(newString) { matchResult ->
-                matchResult.groups[2]?.value ?: ""
-            }
-        }
-
-        return newString.trim()
-    }
-
-    private fun formatBytes(bytes: Long): String {
-        return when {
-            bytes >= 1_000_000_000 -> "%.2f GB".format(bytes / 1_000_000_000.0)
-            bytes >= 1_000_000 -> "%.2f MB".format(bytes / 1_000_000.0)
-            bytes >= 1_000 -> "%.2f KB".format(bytes / 1_000.0)
-            bytes > 1 -> "$bytes bytes"
-            bytes == 1L -> "$bytes byte"
-            else -> ""
-        }
-    }
-
-    private fun getCookie(url: String): String {
-        val cookieList = client.cookieJar.loadForRequest(url.toHttpUrl())
-        return if (cookieList.isNotEmpty()) {
-            cookieList.joinToString("; ") { "${it.name}=${it.value}" }
-        } else {
-            ""
-        }
-    }
-
-    private fun parseStatus(statusString: String): Int {
-        return when (statusString) {
-            "Currently Airing" -> SAnime.ONGOING
-            "Finished Airing" -> SAnime.COMPLETED
-            else -> SAnime.UNKNOWN
-        }
-    }
-
-    companion object {
-        private val ITEM_NUMBER_REGEX = """ - (?:S\d+E)?(\d+)""".toRegex()
-        private val KEY_REGEX = """"(\w{39})"""".toRegex()
-        private val VERSION_REGEX = """"([^"]+web-frontend[^"]+)"""".toRegex()
-        private val JSON_REGEX = """(?:)\s*(\{(.+)\})\s*(?:)""".toRegex(RegexOption.DOT_MATCHES_ALL)
-        private const val BOUNDARY = "=====vc17a3rwnndj====="
-
-        private const val MAX_RECURSION_DEPTH = 2
-
-        private const val TRIM_EPISODE_NAME_KEY = "trim_episode"
-        private const val TRIM_EPISODE_NAME_DEFAULT = true
-    }
-
-    private val SharedPreferences.trimEpisodeName
-        get() = getBoolean(TRIM_EPISODE_NAME_KEY, TRIM_EPISODE_NAME_DEFAULT)
-
-    // ============================== Settings ==============================
-
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        SwitchPreferenceCompat(screen.context).apply {
-            key = TRIM_EPISODE_NAME_KEY
-            title = "Trim info from episode name"
-            setDefaultValue(TRIM_EPISODE_NAME_DEFAULT)
-            setOnPreferenceChangeListener { _, newValue ->
-                preferences.edit().putBoolean(key, newValue as Boolean).commit()
-            }
-        }.also(screen::addPreference)
-    }
-}
diff --git a/src/en/rule34video/res/web_hi_res_500.png b/src/en/rule34video/res/web_hi_res_500.png
deleted file mode 100644
index 7457e072..00000000
Binary files a/src/en/rule34video/res/web_hi_res_500.png and /dev/null differ
diff --git a/src/en/seez/build.gradle b/src/en/seez/build.gradle
deleted file mode 100644
index be94a097..00000000
--- a/src/en/seez/build.gradle
+++ /dev/null
@@ -1,12 +0,0 @@
-ext {
-    extName = 'Seez'
-    extClass = '.Seez'
-    extVersionCode = 11
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(':lib:vidsrc-extractor'))
-    implementation(project(':lib:filemoon-extractor'))
-}
diff --git a/src/en/seez/res/mipmap-hdpi/ic_launcher.png b/src/en/seez/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 41ef17c3..00000000
Binary files a/src/en/seez/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/seez/res/mipmap-mdpi/ic_launcher.png b/src/en/seez/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 40273788..00000000
Binary files a/src/en/seez/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/seez/res/mipmap-xhdpi/ic_launcher.png b/src/en/seez/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 6bf2ef21..00000000
Binary files a/src/en/seez/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/seez/res/mipmap-xxhdpi/ic_launcher.png b/src/en/seez/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 272e5ed3..00000000
Binary files a/src/en/seez/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/seez/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/seez/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 8f181f6b..00000000
Binary files a/src/en/seez/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/seez/res/web_hi_res_512.png b/src/en/seez/res/web_hi_res_512.png
deleted file mode 100644
index 54897318..00000000
Binary files a/src/en/seez/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/seez/src/eu/kanade/tachiyomi/animeextension/en/seez/Seez.kt b/src/en/seez/src/eu/kanade/tachiyomi/animeextension/en/seez/Seez.kt
deleted file mode 100644
index f5ce8d51..00000000
--- a/src/en/seez/src/eu/kanade/tachiyomi/animeextension/en/seez/Seez.kt
+++ /dev/null
@@ -1,434 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.seez
-
-import android.app.Application
-import android.content.SharedPreferences
-import android.util.Base64
-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.AnimeHttpSource
-import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
-import eu.kanade.tachiyomi.lib.vidsrcextractor.VidsrcExtractor
-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 kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.Json
-import okhttp3.HttpUrl
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.Request
-import okhttp3.Response
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import uy.kohesive.injekt.injectLazy
-import java.net.URLDecoder
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-import javax.crypto.Cipher
-import javax.crypto.spec.SecretKeySpec
-
-class Seez : ConfigurableAnimeSource, AnimeHttpSource() {
-
-    override val name = "Seez"
-
-    override val baseUrl = "https://seez.su"
-
-    private val embedUrl = "https://vidsrc.to"
-
-    override val lang = "en"
-
-    override val supportsLatest = false
-
-    private val json: Json by injectLazy()
-
-    private val preferences: SharedPreferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    private val apiKey by lazy {
-        val jsUrl = client.newCall(GET(baseUrl, headers)).execute().asJsoup()
-            .select("script[defer][src]")[1].attr("abs:src")
-
-        val jsBody = client.newCall(GET(jsUrl, headers)).execute().body.string()
-        Regex("""f="(\w{20,})"""").find(jsBody)!!.groupValues[1]
-    }
-
-    private val apiHeaders = headers.newBuilder().apply {
-        add("Accept", "application/json, text/javascript, */*; q=0.01")
-        add("Host", "api.themoviedb.org")
-        add("Origin", baseUrl)
-        add("Referer", "$baseUrl/")
-    }.build()
-
-    // ============================== Popular ===============================
-
-    override fun popularAnimeRequest(page: Int): Request {
-        val url = TMDB_URL.newBuilder().apply {
-            addPathSegment("movie")
-            addPathSegment("popular")
-            addQueryParameter("language", "en-US")
-            addQueryParameter("page", page.toString())
-        }.buildAPIUrl()
-
-        return GET(url, headers = apiHeaders)
-    }
-
-    override fun popularAnimeParse(response: Response): AnimesPage {
-        val data = response.parseAs<TmdbResponse>()
-
-        val animeList = data.results.map { ani ->
-            val name = ani.title ?: ani.name ?: "Title N/A"
-
-            SAnime.create().apply {
-                title = name
-                url = LinkData(ani.id, "movie").toJsonString()
-                thumbnail_url = ani.posterPath?.let { IMG_URL + it } ?: FALLBACK_IMG
-            }
-        }
-
-        return AnimesPage(animeList, data.page < data.totalPages)
-    }
-
-    // =============================== Latest ===============================
-
-    override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
-
-    override fun latestUpdatesParse(response: Response): AnimesPage = throw UnsupportedOperationException()
-
-    // =============================== Search ===============================
-
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
-        val filterList = if (filters.isEmpty()) getFilterList() else filters
-        val typeFilter = filterList.find { it is TypeFilter } as TypeFilter
-        val collectionFilter = filterList.find { it is CollectionFilter } as CollectionFilter
-        val orderFilter = filterList.find { it is OrderFilter } as OrderFilter
-
-        val url = if (query.isNotBlank()) {
-            TMDB_URL.newBuilder().apply {
-                addPathSegment("search")
-                addPathSegment("multi")
-                addQueryParameter("query", query)
-                addQueryParameter("page", page.toString())
-            }.buildAPIUrl()
-        } else {
-            TMDB_URL.newBuilder().apply {
-                addPathSegment(typeFilter.toUriPart())
-                addPathSegment(orderFilter.toUriPart())
-                if (collectionFilter.state != 0) {
-                    addQueryParameter("with_networks", collectionFilter.toUriPart())
-                }
-                addQueryParameter("language", "en-US")
-                addQueryParameter("page", page.toString())
-            }.buildAPIUrl()
-        }
-
-        return GET(url, headers = apiHeaders)
-    }
-
-    override fun searchAnimeParse(response: Response): AnimesPage {
-        val data = response.parseAs<TmdbResponse>()
-
-        val animeList = data.results.map { ani ->
-            val name = ani.title ?: ani.name ?: "Title N/A"
-
-            SAnime.create().apply {
-                title = name
-                url = LinkData(ani.id, ani.mediaType).toJsonString()
-                thumbnail_url = ani.posterPath?.let { IMG_URL + it } ?: FALLBACK_IMG
-                status = if (ani.mediaType == "movie") SAnime.COMPLETED else SAnime.UNKNOWN
-            }
-        }
-
-        return AnimesPage(animeList, data.page < data.totalPages)
-    }
-
-    // ============================== Filters ===============================
-
-    override fun getFilterList(): AnimeFilterList = AnimeFilterList(
-        AnimeFilter.Header("NOTE: Filters are going to be ignored if using search text"),
-        TypeFilter(),
-        CollectionFilter(),
-        OrderFilter(),
-    )
-
-    private class TypeFilter : UriPartFilter(
-        "Type",
-        arrayOf(
-            Pair("Movies", "movie"),
-            Pair("TV-shows", "tv"),
-        ),
-    )
-
-    private class CollectionFilter : UriPartFilter(
-        "Collection",
-        arrayOf(
-            Pair("<select>", ""),
-            Pair("Netflix", "213"),
-            Pair("HBO Max", "49"),
-            Pair("Paramount+", "4330"),
-            Pair("Disney+", "2739"),
-            Pair("Apple TV+", "2552"),
-            Pair("Prime Video", "1024"),
-        ),
-    )
-
-    private class OrderFilter : UriPartFilter(
-        "Order by",
-        arrayOf(
-            Pair("Popular", "popular"),
-            Pair("Top", "top_rated"),
-        ),
-    )
-
-    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
-    }
-
-    // =========================== Anime Details ============================
-
-    override fun animeDetailsRequest(anime: SAnime): Request {
-        val data = json.decodeFromString<LinkData>(anime.url)
-
-        val url = TMDB_URL.newBuilder().apply {
-            addPathSegment(data.mediaType)
-            addPathSegment(data.id.toString())
-        }.buildAPIUrl()
-
-        return GET(url, headers = apiHeaders)
-    }
-
-    override fun animeDetailsParse(response: Response): SAnime {
-        val data = response.parseAs<TmdbDetailsResponse>()
-
-        return SAnime.create().apply {
-            author = data.productions?.joinToString { it.name }
-            genre = data.genres?.joinToString { it.name }
-            status = when (data.status) {
-                "Ended", "Released" -> SAnime.COMPLETED
-                "In Production" -> SAnime.LICENSED
-                "Canceled" -> SAnime.CANCELLED
-                "Returning Series" -> {
-                    data.nextEpisode?.let { SAnime.ONGOING } ?: SAnime.ON_HIATUS
-                }
-                else -> SAnime.UNKNOWN
-            }
-            description = buildString {
-                data.overview?.let {
-                    appendLine(it)
-                    appendLine()
-                }
-                data.nextEpisode?.let {
-                    appendLine("Next: Ep ${it.epNumber} - ${it.name}")
-                    appendLine("Air Date: ${it.airDate}")
-                    appendLine()
-                }
-                data.releaseDate?.let { appendLine("Release date: $it") }
-                data.firstAirDate?.let { appendLine("First air date: $it") }
-                data.lastAirDate?.let { appendLine("Last air date: $it") }
-                data.languages?.let { langs ->
-                    append("Languages: ")
-                    appendLine(langs.joinToString { "${it.engName} (${it.name})" })
-                }
-            }
-        }
-    }
-
-    // ============================== Episodes ==============================
-
-    override fun episodeListRequest(anime: SAnime): Request = animeDetailsRequest(anime)
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val data = response.parseAs<TmdbDetailsResponse>()
-        val episodeList = mutableListOf<SEpisode>()
-
-        if (data.title != null) { // movie
-            episodeList.add(
-                SEpisode.create().apply {
-                    name = "Movie"
-                    date_upload = parseDate(data.releaseDate!!)
-                    episode_number = 1F
-                    url = "/movie/${data.id}"
-                },
-            )
-        } else {
-            data.seasons.filter { t -> t.seasonNumber != 0 }.forEach { season ->
-                val seasonUrl = TMDB_URL.newBuilder().apply {
-                    addPathSegment("tv")
-                    addPathSegment(data.id.toString())
-                    addPathSegment("season")
-                    addPathSegment(season.seasonNumber.toString())
-                }.buildAPIUrl()
-
-                val seasonData = client.newCall(
-                    GET(seasonUrl, headers = apiHeaders),
-                ).execute().parseAs<TmdbSeasonResponse>()
-
-                seasonData.episodes.filter { ep ->
-                    ep.airDate?.let {
-                        DATE_FORMATTER.parse(it)!! <= DATE_FORMATTER.parse(DATE_FORMATTER.format(Date()))
-                    } ?: false
-                }.forEach { ep ->
-                    episodeList.add(
-                        SEpisode.create().apply {
-                            name = "Season ${season.seasonNumber} Ep. ${ep.epNumber} - ${ep.name}"
-                            date_upload = ep.airDate?.let(::parseDate) ?: 0L
-                            episode_number = ep.epNumber.toFloat()
-                            url = "/tv/${data.id}/${season.seasonNumber}/${ep.epNumber}"
-                        },
-                    )
-                }
-            }
-        }
-
-        return episodeList.reversed()
-    }
-
-    // ============================ Video Links =============================
-
-    private val vidsrcExtractor by lazy { VidsrcExtractor(client, headers) }
-    private val filemoonExtractor by lazy { FilemoonExtractor(client) }
-
-    override fun videoListRequest(episode: SEpisode): Request {
-        val docHeaders = headers.newBuilder().apply {
-            add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
-            add("Host", embedUrl.toHttpUrl().host)
-            add("Referer", "$baseUrl/")
-        }.build()
-
-        return GET("$embedUrl/embed${episode.url}", headers = docHeaders)
-    }
-
-    override fun videoListParse(response: Response): List<Video> {
-        val document = response.asJsoup()
-
-        val sourcesHeaders = headers.newBuilder().apply {
-            add("Accept", "application/json, text/javascript, */*; q=0.01")
-            add("Host", embedUrl.toHttpUrl().host)
-            add("Referer", response.request.url.toString())
-            add("X-Requested-With", "XMLHttpRequest")
-        }.build()
-
-        val dataId = document.selectFirst("ul.episodes li a[data-id]")!!.attr("data-id")
-        val sources = client.newCall(
-            GET("$embedUrl/ajax/embed/episode/$dataId/sources", headers = sourcesHeaders),
-        ).execute().parseAs<EmbedSourceList>().result
-
-        val urlList = sources.map {
-            val encrypted = client.newCall(
-                GET("$embedUrl/ajax/embed/source/${it.id}", headers = sourcesHeaders),
-            ).execute().parseAs<EmbedUrlResponse>().result.url
-
-            Pair(decrypt(encrypted), it.title)
-        }
-
-        return urlList.parallelCatchingFlatMapBlocking {
-            val url = it.first
-
-            when (val name = it.second) {
-                "Vidplay" -> vidsrcExtractor.videosFromUrl(url, name)
-                "Filemoon" -> filemoonExtractor.videosFromUrl(url)
-                else -> emptyList()
-            }
-        }
-    }
-
-    // ============================= Utilities ==============================
-
-    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) },
-                { it.quality.contains(quality) },
-                { Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
-            ),
-        ).reversed()
-    }
-
-    private fun HttpUrl.Builder.buildAPIUrl(): String = this.apply {
-        addQueryParameter("api_key", apiKey)
-    }.build().toString()
-
-    private fun LinkData.toJsonString(): String {
-        return json.encodeToString(this)
-    }
-
-    private fun parseDate(dateStr: String): Long {
-        return runCatching { DATE_FORMATTER.parse(dateStr)?.time }
-            .getOrNull() ?: 0L
-    }
-
-    private fun decrypt(encrypted: String): String {
-        var vrf = encrypted.toByteArray()
-        vrf = Base64.decode(vrf, Base64.URL_SAFE)
-
-        val rc4Key = SecretKeySpec("8z5Ag5wgagfsOuhz".toByteArray(), "RC4")
-        val cipher = Cipher.getInstance("RC4")
-        cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
-        vrf = cipher.doFinal(vrf)
-
-        return URLDecoder.decode(vrf.toString(Charsets.UTF_8), "utf-8")
-    }
-
-    companion object {
-        private val TMDB_URL = "https://api.themoviedb.org/3".toHttpUrl()
-        private const val IMG_URL = "https://image.tmdb.org/t/p/w300/"
-        private const val FALLBACK_IMG = "https://seez.su/fallback.png"
-
-        private val DATE_FORMATTER by lazy {
-            SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
-        }
-
-        private const val PREF_QUALITY_KEY = "preferred_quality"
-        private const val PREF_QUALITY_DEFAULT = "1080"
-
-        private const val PREF_SERVER_KEY = "preferred_server"
-        private const val PREF_SERVER_DEFAULT = "Vidplay"
-    }
-    // ============================== Settings ==============================
-
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        ListPreference(screen.context).apply {
-            key = PREF_QUALITY_KEY
-            title = "Preferred quality"
-            entries = arrayOf("1080p", "720p", "480p", "360p")
-            entryValues = arrayOf("1080", "720", "480", "360")
-            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 = arrayOf("Vidplay", "Filemoon")
-            entryValues = arrayOf("Vidplay", "Filemoon")
-            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)
-    }
-}
diff --git a/src/en/seez/src/eu/kanade/tachiyomi/animeextension/en/seez/SeezDto.kt b/src/en/seez/src/eu/kanade/tachiyomi/animeextension/en/seez/SeezDto.kt
deleted file mode 100644
index b56b2fcc..00000000
--- a/src/en/seez/src/eu/kanade/tachiyomi/animeextension/en/seez/SeezDto.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.seez
-
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-
-@Serializable
-data class TmdbResponse(
-    val page: Int,
-    @SerialName("total_pages")
-    val totalPages: Int,
-    val results: List<TmdbResult>,
-) {
-    @Serializable
-    data class TmdbResult(
-        val id: Int,
-        @SerialName("media_type")
-        val mediaType: String = "tv",
-        @SerialName("poster_path")
-        val posterPath: String? = null,
-        val title: String? = null,
-        val name: String? = null,
-    )
-}
-
-@Serializable
-data class TmdbDetailsResponse(
-    val id: Int,
-    val overview: String? = null,
-    val genres: List<GenreObject>? = null,
-    @SerialName("release_date")
-    val releaseDate: String? = null,
-    @SerialName("first_air_date")
-    val firstAirDate: String? = null,
-    @SerialName("last_air_date")
-    val lastAirDate: String? = null,
-    val name: String? = null,
-    val title: String? = null,
-    val seasons: List<SeasonObject> = emptyList(),
-    val status: String,
-    @SerialName("next_episode_to_air")
-    val nextEpisode: NextEpisode? = null,
-    @SerialName("production_companies")
-    val productions: List<Company>? = null,
-    @SerialName("spoken_languages")
-    val languages: List<Language>? = null,
-) {
-    @Serializable
-    data class GenreObject(
-        val name: String,
-    )
-
-    @Serializable
-    data class SeasonObject(
-        @SerialName("season_number")
-        val seasonNumber: Int,
-    )
-
-    @Serializable
-    data class NextEpisode(
-        val name: String? = "",
-        @SerialName("episode_number")
-        val epNumber: Int,
-        @SerialName("air_date")
-        val airDate: String,
-    )
-
-    @Serializable
-    data class Company(
-        val name: String,
-        @SerialName("origin_country")
-        val originCountry: String,
-    )
-
-    @Serializable
-    data class Language(
-        val name: String,
-        @SerialName("english_name")
-        val engName: String,
-    )
-}
-
-@Serializable
-data class TmdbSeasonResponse(
-    val episodes: List<EpisodeObject>,
-) {
-    @Serializable
-    data class EpisodeObject(
-        @SerialName("episode_number")
-        val epNumber: Int,
-        val name: String,
-        @SerialName("air_date")
-        val airDate: String? = null,
-    )
-}
-
-@Serializable
-data class LinkData(
-    val id: Int,
-    @SerialName("media_type")
-    val mediaType: String,
-)
-
-@Serializable
-data class EmbedSourceList(
-    val result: List<EmbedSource>,
-) {
-    @Serializable
-    data class EmbedSource(
-        val id: String,
-        val title: String,
-    )
-}
-
-@Serializable
-data class EmbedUrlResponse(
-    val result: EmbedUrlObject,
-) {
-    @Serializable
-    data class EmbedUrlObject(
-        val url: String,
-    )
-}
diff --git a/src/en/sflix/res/play_store_512.png b/src/en/sflix/res/play_store_512.png
deleted file mode 100644
index 6e157d8a..00000000
Binary files a/src/en/sflix/res/play_store_512.png and /dev/null differ
diff --git a/src/en/slothanime/build.gradle b/src/en/slothanime/build.gradle
deleted file mode 100644
index 162ed52d..00000000
--- a/src/en/slothanime/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-ext {
-    extName = 'SlothAnime'
-    extClass = '.SlothAnime'
-    extVersionCode = 2
-}
-
-apply from: "$rootDir/common.gradle"
\ No newline at end of file
diff --git a/src/en/slothanime/res/mipmap-hdpi/ic_launcher.png b/src/en/slothanime/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 69936bbf..00000000
Binary files a/src/en/slothanime/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/slothanime/res/mipmap-mdpi/ic_launcher.png b/src/en/slothanime/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index c67325c1..00000000
Binary files a/src/en/slothanime/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/slothanime/res/mipmap-xhdpi/ic_launcher.png b/src/en/slothanime/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 977c68e7..00000000
Binary files a/src/en/slothanime/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/slothanime/res/mipmap-xxhdpi/ic_launcher.png b/src/en/slothanime/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index efeb924b..00000000
Binary files a/src/en/slothanime/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/slothanime/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/slothanime/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 22b3b120..00000000
Binary files a/src/en/slothanime/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/slothanime/src/eu/kanade/tachiyomi/animeextension/en/slothanime/Filters.kt b/src/en/slothanime/src/eu/kanade/tachiyomi/animeextension/en/slothanime/Filters.kt
deleted file mode 100644
index fa496d49..00000000
--- a/src/en/slothanime/src/eu/kanade/tachiyomi/animeextension/en/slothanime/Filters.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.slothanime
-
-import eu.kanade.tachiyomi.animesource.model.AnimeFilter
-
-open class UriPartFilter(
-    name: String,
-    private val vals: Array<Pair<String, String>>,
-    defaultValue: String? = null,
-) : AnimeFilter.Select<String>(
-    name,
-    vals.map { it.first }.toTypedArray(),
-    vals.indexOfFirst { it.second == defaultValue }.takeIf { it != -1 } ?: 0,
-) {
-    fun getValue(): String {
-        return vals[state].second
-    }
-}
-
-open class UriMultiSelectOption(name: String, val value: String) : AnimeFilter.CheckBox(name)
-
-open class UriMultiSelectFilter(
-    name: String,
-    private val vals: Array<Pair<String, String>>,
-) : AnimeFilter.Group<UriMultiSelectOption>(name, vals.map { UriMultiSelectOption(it.first, it.second) }) {
-    fun getValues(): List<String> {
-        return state.filter { it.state }.map { it.value }
-    }
-}
-
-open class UriMultiTriSelectOption(name: String, val value: String) : AnimeFilter.TriState(name)
-
-open class UriMultiTriSelectFilter(
-    name: String,
-    private val vals: Array<Pair<String, String>>,
-) : AnimeFilter.Group<UriMultiTriSelectOption>(name, vals.map { UriMultiTriSelectOption(it.first, it.second) }) {
-    fun getIncluded(): List<String> {
-        return state.filter { it.state == TriState.STATE_INCLUDE }.map { it.value }
-    }
-
-    fun getExcluded(): List<String> {
-        return state.filter { it.state == TriState.STATE_EXCLUDE }.map { it.value }
-    }
-}
-
-class GenreFilter : UriMultiTriSelectFilter(
-    "Genre",
-    arrayOf(
-        Pair("Action", "action"),
-        Pair("Adventure", "adventure"),
-        Pair("Fantasy", "fantasy"),
-        Pair("Martial Arts", "martial_arts"),
-        Pair("Comedy", "comedy"),
-        Pair("School", "school"),
-        Pair("Slice of Life", "slice_of_life"),
-        Pair("Military", "military"),
-        Pair("Sci-Fi", "scifi"),
-        Pair("Isekai", "isekai"),
-        Pair("Kids", "kids"),
-        Pair("Iyashikei", "iyashikei"),
-        Pair("Horror", "horror"),
-        Pair("Supernatural", "supernatural"),
-        Pair("Avant Garde", "avant_garde"),
-        Pair("Demons", "demons"),
-        Pair("Gourmet", "gourmet"),
-        Pair("Music", "music"),
-        Pair("Drama", "drama"),
-        Pair("Seinen", "seinen"),
-        Pair("Ecchi", "ecchi"),
-        Pair("Harem", "harem"),
-        Pair("Romance", "romance"),
-        Pair("Magic", "magic"),
-        Pair("Mystery", "mystery"),
-        Pair("Suspense", "suspense"),
-        Pair("Parody", "parody"),
-        Pair("Psychological", "psychological"),
-        Pair("Super Power", "super_power"),
-        Pair("Vampire", "vampire"),
-        Pair("Shounen", "shounen"),
-        Pair("Space", "space"),
-        Pair("Mecha", "mecha"),
-        Pair("Sports", "sports"),
-        Pair("Shoujo", "shoujo"),
-        Pair("Girls Love", "girls_love"),
-        Pair("Josei", "josei"),
-        Pair("Mahou Shoujo", "mahou_shoujo"),
-        Pair("Thriller", "thriller"),
-        Pair("Reverse Harem", "reverse_harem"),
-        Pair("Boys Love", "boys_love"),
-        Pair("Uncategorized", "uncategorized"),
-    ),
-)
-
-class TypeFilter : UriMultiSelectFilter(
-    "Type",
-    arrayOf(
-        Pair("ONA", "ona"),
-        Pair("TV", "tv"),
-        Pair("MOVIE", "movie"),
-        Pair("SPECIAL", "special"),
-        Pair("OVA", "ova"),
-        Pair("MUSIC", "music"),
-    ),
-)
-
-class StatusFilter : UriPartFilter(
-    "Status",
-    arrayOf(
-        Pair("All", "2"),
-        Pair("Completed", "1"),
-        Pair("Releasing", "0"),
-    ),
-)
-
-class SortFilter : UriPartFilter(
-    "Sort",
-    arrayOf(
-        Pair("Most Watched", "viewed"),
-        Pair("Scored", "scored"),
-        Pair("Newest", "created_at"),
-        Pair("Latest Update", "updated_at"),
-    ),
-)
diff --git a/src/en/slothanime/src/eu/kanade/tachiyomi/animeextension/en/slothanime/SlothAnime.kt b/src/en/slothanime/src/eu/kanade/tachiyomi/animeextension/en/slothanime/SlothAnime.kt
deleted file mode 100644
index b54f381a..00000000
--- a/src/en/slothanime/src/eu/kanade/tachiyomi/animeextension/en/slothanime/SlothAnime.kt
+++ /dev/null
@@ -1,197 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.en.slothanime
-
-import android.util.Base64
-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 okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.Request
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-import javax.crypto.Cipher
-import javax.crypto.spec.IvParameterSpec
-import javax.crypto.spec.SecretKeySpec
-import kotlin.math.floor
-
-class SlothAnime : ParsedAnimeHttpSource() {
-
-    override val name = "SlothAnime"
-
-    override val baseUrl = "https://slothanime.com"
-
-    override val lang = "en"
-
-    override val supportsLatest = true
-
-    // ============================== Popular ===============================
-
-    override fun popularAnimeRequest(page: Int): Request {
-        val url = if (page > 1) {
-            "$baseUrl/list/viewed?page=$page"
-        } else {
-            "$baseUrl/list/viewed"
-        }
-
-        return GET(url, headers)
-    }
-
-    override fun popularAnimeSelector(): String = ".row > div > .anime-card-md"
-
-    override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
-        thumbnail_url = element.selectFirst("img")!!.imgAttr()
-        with(element.selectFirst("a[href~=/anime]")!!) {
-            title = text()
-            setUrlWithoutDomain(attr("abs:href"))
-        }
-    }
-
-    override fun popularAnimeNextPageSelector(): String = ".pagination > .active ~ li:has(a)"
-
-    // =============================== Latest ===============================
-
-    override fun latestUpdatesRequest(page: Int): Request {
-        val url = if (page > 1) {
-            "$baseUrl/list/latest?page=$page"
-        } else {
-            "$baseUrl/list/latest"
-        }
-
-        return GET(url, headers)
-    }
-    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 genreFilter = filters.filterIsInstance<GenreFilter>().first()
-        val typeFilter = filters.filterIsInstance<TypeFilter>().first()
-        val statusFilter = filters.filterIsInstance<StatusFilter>().first()
-        val sortFilter = filters.filterIsInstance<SortFilter>().first()
-
-        val url = baseUrl.toHttpUrl().newBuilder().apply {
-            addPathSegment("search")
-            addQueryParameter("q", query)
-            genreFilter.getIncluded().forEachIndexed { idx, value ->
-                addQueryParameter("genre[$idx]", value)
-            }
-            typeFilter.getValues().forEachIndexed { idx, value ->
-                addQueryParameter("type[$idx]", value)
-            }
-            addQueryParameter("status", statusFilter.getValue())
-            addQueryParameter("sort", sortFilter.getValue())
-            genreFilter.getExcluded().forEachIndexed { idx, value ->
-                addQueryParameter("ignore_genre[$idx]", value)
-            }
-
-            if (page > 1) {
-                addQueryParameter("page", page.toString())
-            }
-        }.build()
-
-        return GET(url, headers)
-    }
-
-    override fun searchAnimeSelector(): String = popularAnimeSelector()
-
-    override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
-
-    override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
-
-    // ============================== Filters ===============================
-
-    override fun getFilterList(): AnimeFilterList = AnimeFilterList(
-        GenreFilter(),
-        TypeFilter(),
-        StatusFilter(),
-        SortFilter(),
-    )
-
-    // =========================== Anime Details ============================
-
-    override fun animeDetailsParse(document: Document): SAnime = SAnime.create().apply {
-        title = document.selectFirst(".single-title > *:not(.single-altername)")!!.text()
-        thumbnail_url = document.selectFirst(".single-cover > img")!!.imgAttr()
-        description = document.selectFirst(".single-detail:has(span:contains(Description)) .more-content")?.text()
-        genre = document.select(".single-tag > a.tag").joinToString { it.text() }
-        author = document.select(".single-detail:has(span:contains(Studios)) .value a").joinToString { it.text() }
-    }
-
-    // ============================== Episodes ==============================
-
-    override fun episodeListSelector() = ".list-episodes-container > a[class~=episode]"
-
-    override fun episodeFromElement(element: Element): SEpisode = SEpisode.create().apply {
-        setUrlWithoutDomain(element.attr("abs:href"))
-        name = element.text()
-            .replace(Regex("""^EP """), "Episode ")
-            .replace(Regex("""^\d+""")) { m -> "Episode ${m.value}" }
-    }
-
-    // ============================ Video Links =============================
-
-    fun encryptAES(input: String, key: ByteArray, iv: ByteArray): String {
-        val cipher = Cipher.getInstance("AES/CBC/NoPadding")
-        val secretKey = SecretKeySpec(key, "AES")
-        val ivParameterSpec = IvParameterSpec(iv)
-
-        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec)
-        val paddedInput = zeroPad(input)
-        val encryptedBytes = cipher.doFinal(paddedInput.toByteArray(Charsets.UTF_8))
-        return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP)
-    }
-
-    fun zeroPad(input: String): String {
-        val blockSize = 16
-        val padLength = blockSize - input.length % blockSize
-        return input.padEnd(input.length + padLength, '\u0000')
-    }
-
-    override suspend fun getVideoList(episode: SEpisode): List<Video> {
-        val key = String(Base64.decode(KEY, Base64.DEFAULT)).chunked(2).map { it.toInt(16).toByte() }.toByteArray()
-        val iv = String(Base64.decode(IV, Base64.DEFAULT)).chunked(2).map { it.toInt(16).toByte() }.toByteArray()
-        val time = floor(System.currentTimeMillis() / 1000.0)
-        val vrf = encryptAES(time.toString(), key, iv)
-        val id = episode.url.substringAfterLast("/")
-
-        val url = baseUrl.toHttpUrl().newBuilder().apply {
-            addPathSegment("player-url")
-            addPathSegment(id)
-            addQueryParameter("vrf", vrf)
-        }.build().toString()
-
-        val videoHeaders = headersBuilder().apply {
-            add("Accept", "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5")
-            add("Referer", baseUrl + episode.url)
-        }.build()
-
-        return listOf(
-            Video(url, "Video", url, videoHeaders),
-        )
-    }
-
-    override fun videoListSelector() = throw UnsupportedOperationException()
-
-    override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
-
-    override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
-
-    // ============================= Utilities ==============================
-
-    private fun Element.imgAttr(): String = when {
-        hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
-        hasAttr("data-src") -> attr("abs:data-src")
-        else -> attr("abs:src")
-    }
-
-    companion object {
-        private const val KEY = "YWI0OWZkYjllYzE5M2I0YWQzYWFkMGVmMTU4N2Q2OGE0YmYxY2Y5YjJkMjA4YjRjYzIzMDYwZTkwNThiMjA0NA=="
-        private const val IV = "NDI4MzEzNjcxMThiMzFmYjVhNTI1MTMzNTc0ZmJmNGI="
-    }
-}
diff --git a/src/en/superstream/res/web_hi_res_512.png b/src/en/superstream/res/web_hi_res_512.png
deleted file mode 100644
index bd2158b7..00000000
Binary files a/src/en/superstream/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/uhdmovies/res/web_hi_res_512.png b/src/en/uhdmovies/res/web_hi_res_512.png
deleted file mode 100644
index 42b1f7c6..00000000
Binary files a/src/en/uhdmovies/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/uniquestream/res/web_hi_res_512.png b/src/en/uniquestream/res/web_hi_res_512.png
deleted file mode 100644
index 73a3a7b0..00000000
Binary files a/src/en/uniquestream/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/wcofun/res/web_hi_res_512.png b/src/en/wcofun/res/web_hi_res_512.png
deleted file mode 100644
index fe248b2d..00000000
Binary files a/src/en/wcofun/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/wcostream/res/play_store_512.png b/src/en/wcostream/res/play_store_512.png
deleted file mode 100644
index b2e10c2a..00000000
Binary files a/src/en/wcostream/res/play_store_512.png and /dev/null differ
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/animefenix/build.gradle b/src/es/animefenix/build.gradle
index be565367..38c7f15a 100644
--- a/src/es/animefenix/build.gradle
+++ b/src/es/animefenix/build.gradle
@@ -1,7 +1,7 @@
 ext {
     extName = 'Animefenix'
     extClass = '.Animefenix'
-    extVersionCode = 55
+    extVersionCode = 57
 }
 
 apply from: "$rootDir/common.gradle"
@@ -17,9 +17,9 @@ dependencies {
     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'))
+    implementation(project(':lib:amazon-extractor'))
 }
diff --git a/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/AnimeFenixFilters.kt b/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/AnimeFenixFilters.kt
index eaab6fbf..5bdebcd9 100644
--- a/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/AnimeFenixFilters.kt
+++ b/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/AnimeFenixFilters.kt
@@ -12,30 +12,6 @@ object AnimeFenixFilters {
         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)
     }
@@ -51,111 +27,118 @@ object AnimeFenixFilters {
     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"),
+            filters.asQueryPart<StateFilter>("estado") +
+                filters.asQueryPart<TypesFilter>("tipo") +
+                filters.asQueryPart<GenresFilter>("genero") +
+                filters.asQueryPart<YearsFilter>("estreno") +
+                filters.asQueryPart<LangFilter>("idioma"),
         )
     }
 
     val FILTER_LIST get() = AnimeFilterList(
         AnimeFilter.Header("La busqueda por texto ignora el filtro"),
+        StateFilter(),
+        TypesFilter(),
         GenresFilter(),
         YearsFilter(),
-        TypesFilter(),
-        StateFilter(),
-        SortFilter(),
+        LangFilter(),
     )
 
-    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)
+    class StateFilter : QueryPartFilter("Estado", AnimeFenixFiltersData.STATE)
+    class TypesFilter : QueryPartFilter("Tipo", AnimeFenixFiltersData.TYPES)
+    class GenresFilter : QueryPartFilter("Género", AnimeFenixFiltersData.GENRES)
+    class YearsFilter : QueryPartFilter("Año", AnimeFenixFiltersData.YEARS)
+    class LangFilter : QueryPartFilter("Idioma", AnimeFenixFiltersData.LANGUAGE)
 
     private object AnimeFenixFiltersData {
-        val YEARS = (1990..Calendar.getInstance().get(Calendar.YEAR)).map { Pair("$it", "$it") }.reversed().toTypedArray()
+        val STATE = arrayOf(
+            Pair("Todos", ""),
+            Pair("Emisión", "2"),
+            Pair("Finalizado", "1"),
+            Pair("Próximamente", "3"),
+        )
 
         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"),
+            Pair("Todos", ""),
+            Pair("TV", "1"),
+            Pair("Película", "2"),
+            Pair("OVA", "3"),
+            Pair("Especial", "4"),
+            Pair("Serie", "9"),
+            Pair("Dorama", "11"),
+            Pair("Corto", "14"),
+            Pair("Donghua", "15"),
+            Pair("ONA", "16"),
+            Pair("Live Action", "17"),
+            Pair("Manhwa", "18"),
+            Pair("Teatral", "19"),
         )
 
         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"),
+            Pair("Todos", ""),
+            Pair("Acción", "1"),
+            Pair("Escolares", "2"),
+            Pair("Romance", "3"),
+            Pair("Shoujo", "4"),
+            Pair("Comedia", "5"),
+            Pair("Drama", "6"),
+            Pair("Seinen", "7"),
+            Pair("Deportes", "8"),
+            Pair("Shounen", "9"),
+            Pair("Recuentos de la vida", "10"),
+            Pair("Ecchi", "11"),
+            Pair("Sobrenatural", "12"),
+            Pair("Fantasía", "13"),
+            Pair("Magia", "14"),
+            Pair("Superpoderes", "15"),
+            Pair("Demencia", "16"),
+            Pair("Misterio", "17"),
+            Pair("Psicológico", "18"),
+            Pair("Suspenso", "19"),
+            Pair("Ciencia Ficción", "20"),
+            Pair("Mecha", "21"),
+            Pair("Militar", "22"),
+            Pair("Aventuras", "23"),
+            Pair("Historico", "24"),
+            Pair("Infantil", "25"),
+            Pair("Artes Marciales", "26"),
+            Pair("Terror", "27"),
+            Pair("Harem", "28"),
+            Pair("Josei", "29"),
+            Pair("Parodia", "30"),
+            Pair("Policía", "31"),
+            Pair("Juegos", "32"),
+            Pair("Carreras", "33"),
+            Pair("Samurai", "34"),
+            Pair("Espacial", "35"),
+            Pair("Música", "36"),
+            Pair("Yuri", "37"),
+            Pair("Demonios", "38"),
+            Pair("Vampiros", "39"),
+            Pair("Yaoi", "40"),
+            Pair("Humor Negro", "41"),
+            Pair("Crimen", "42"),
+            Pair("Hentai", "43"),
+            Pair("Youtuber", "44"),
+            Pair("MaiNess Random", "45"),
+            Pair("Donghua", "46"),
+            Pair("Horror", "47"),
+            Pair("Sin Censura", "48"),
+            Pair("Gore", "49"),
+            Pair("Live Action", "50"),
+            Pair("Isekai", "51"),
+            Pair("Gourmet", "52"),
+            Pair("spokon", "53"),
+            Pair("Zombies", "54"),
+            Pair("Idols", "55"),
         )
+
+        val LANGUAGE = arrayOf(
+            Pair("Todos", ""),
+            Pair("Japonés", "1"),
+            Pair("Latino", "2"),
+        )
+
+        val YEARS = arrayOf(Pair("Todos", "")) + (1967..Calendar.getInstance().get(Calendar.YEAR)).map { Pair("$it", "$it") }.reversed().toTypedArray()
     }
 }
diff --git a/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/Animefenix.kt b/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/Animefenix.kt
index dc1487bd..adb7fef5 100644
--- a/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/Animefenix.kt
+++ b/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/Animefenix.kt
@@ -11,9 +11,9 @@ 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.amazonextractor.AmazonExtractor
 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
@@ -28,37 +28,35 @@ 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 eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
 import okhttp3.Request
 import okhttp3.Response
+import org.jsoup.nodes.Element
+import org.jsoup.select.Elements
 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 baseUrl = "https://animefenix2.tv"
 
     override val lang = "es"
 
-    override val supportsLatest = true
+    override val supportsLatest = false
 
-    private val preferences: SharedPreferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) }
+    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 const val PREF_SERVER_DEFAULT = "Mp4Upload"
         private val SERVER_LIST = arrayOf(
             "YourUpload", "Voe", "Mp4Upload", "Doodstream",
             "Upload", "BurstCloud", "Upstream", "StreamTape",
@@ -67,33 +65,45 @@ class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
         )
     }
 
-    override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/animes?order=likes&page=$page")
+    override fun animeDetailsParse(response: Response): SAnime {
+        val document = response.asJsoup()
+        val animeDetails = SAnime.create().apply {
+            title = document.selectFirst("h1.text-4xl")?.ownText() ?: ""
+            status = document.select(".relative .rounded").getStatus()
+            description = document.selectFirst(".mb-6 p.text-gray-300")?.text()
+            genre = document.select(".flex-wrap a").joinToString { it.text().trim() }
+            thumbnail_url = document.selectFirst("#anime_image")?.getImageUrl()
+        }
+        return animeDetails
+    }
+
+    override fun popularAnimeRequest(page: Int) = GET("$baseUrl/directorio/anime?p=$page&estado=2", headers)
 
     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 elements = document.select(".grid-animes li article a")
+        val nextPage = document.select(".right:not(.disabledd)").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")
+                setUrlWithoutDomain(element.attr("abs:href"))
+                title = element.selectFirst("p:not(.gray)")?.text() ?: ""
+                thumbnail_url = element.selectFirst(".main-img img")?.getImageUrl()
             }
         }
         return AnimesPage(animeList, nextPage)
     }
 
-    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/animes?order=added&page=$page")
+    override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
 
-    override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
+    override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
 
     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")
+            query.isNotBlank() -> GET("$baseUrl/directorio/anime?q=$query&p=$page", headers)
+            params.filter.isNotBlank() -> GET("$baseUrl/directorio/anime${params.getQuery()}&page=$page", headers)
+            else -> popularAnimeRequest(page)
         }
     }
 
@@ -101,115 +111,77 @@ class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
 
     override fun episodeListParse(response: Response): List<SEpisode> {
         val document = response.asJsoup()
-        return document.select("div.container > div > ul > li").map { element ->
+        return document.select(".divide-y li > a").map {
+            val title = it.select(".font-semibold").text()
             SEpisode.create().apply {
-                name = element.selectFirst("span > span")!!.ownText()
-                setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href"))
+                name = title
+                episode_number = title.substringAfter("Episodio").toFloatOrNull() ?: 0F
+                setUrlWithoutDomain(it.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") }
+        val script = document.selectFirst("script:containsData(var tabsArray)") ?: return emptyList()
+        return script.data().substringAfter("<iframe").split("src='")
+            .map { it.substringBefore("'").substringAfter("redirect.php?id=").trim() }
+            .parallelCatchingFlatMapBlocking { url ->
+                serverVideoResolver(url)
+            }
     }
 
+    /*-------------------------------- Video extractors ------------------------------------*/
+    private val universalExtractor by lazy { UniversalExtractor(client) }
+    private val voeExtractor by lazy { VoeExtractor(client) }
+    private val okruExtractor by lazy { OkruExtractor(client) }
+    private val filemoonExtractor by lazy { FilemoonExtractor(client) }
+    private val uqloadExtractor by lazy { UqloadExtractor(client) }
+    private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
+    private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
+    private val doodExtractor by lazy { DoodExtractor(client) }
+    private val streamlareExtractor by lazy { StreamlareExtractor(client) }
+    private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
+    private val burstcloudExtractor by lazy { BurstCloudExtractor(client) }
+    private val upstreamExtractor by lazy { UpstreamExtractor(client) }
+    private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
+    private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client, headers) }
+    private val filelionsExtractor by lazy { StreamWishExtractor(client, headers) }
+    private val amazonExtractor by lazy { AmazonExtractor(client) }
+
     private fun serverVideoResolver(url: String): List<Video> {
-        val videoList = mutableListOf<Video>()
-        val embedUrl = url.lowercase()
-        try {
+        return runCatching {
             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") -> {
+                arrayOf("voe", "robertordercharacter", "donaldlineelse").any(url) -> voeExtractor.videosFromUrl(url)
+                arrayOf("amazon", "amz").any(url) -> amazonExtractor.videosFromUrl(url)
+                arrayOf("ok.ru", "okru").any(url) -> okruExtractor.videosFromUrl(url)
+                arrayOf("moon").any(url) -> filemoonExtractor.videosFromUrl(url, prefix = "Filemoon:")
+                arrayOf("uqload").any(url) -> uqloadExtractor.videosFromUrl(url)
+                arrayOf("mp4upload").any(url) -> mp4uploadExtractor.videosFromUrl(url, headers)
+                arrayOf("wish").any(url) -> streamwishExtractor.videosFromUrl(url, videoNameGen = { "StreamWish:$it" })
+                arrayOf("doodstream", "dood.").any(url) -> doodExtractor.videosFromUrl(url, "DoodStream")
+                arrayOf("streamlare").any(url) -> streamlareExtractor.videosFromUrl(url)
+                arrayOf("yourupload", "upload").any(url) -> yourUploadExtractor.videoFromUrl(url, headers = headers)
+                arrayOf("burstcloud", "burst").any(url) -> burstcloudExtractor.videoFromUrl(url, headers = headers)
+                arrayOf("upstream").any(url) -> upstreamExtractor.videosFromUrl(url)
+                arrayOf("streamtape", "stp", "stape").any(url) -> streamTapeExtractor.videosFromUrl(url)
+                arrayOf("ahvsh", "streamhide").any(url) -> streamHideVidExtractor.videosFromUrl(url)
+                arrayOf("/stream/fl.php").any(url) -> {
                     val video = url.substringAfter("/stream/fl.php?v=")
                     if (client.newCall(GET(video)).execute().code == 200) {
-                        videoList.add(Video(video, "FireLoad", video))
+                        listOf(Video(video, "FireLoad", video))
+                    } else {
+                        emptyList()
                     }
                 }
-                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) }
+                arrayOf("lion").any(url) -> filelionsExtractor.videosFromUrl(url, videoNameGen = { "FileLions:$it" })
+                else -> universalExtractor.videosFromUrl(url, headers)
             }
-        } catch (_: Exception) { }
-        return videoList
+        }.getOrElse { emptyList() }
     }
 
+    private fun Array<String>.any(url: String): Boolean = this.any { url.contains(it, ignoreCase = true) }
+
     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)!!
@@ -222,56 +194,32 @@ class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
         ).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 {
+    private fun Elements.getStatus(): Int {
         return when {
-            statusString.contains("Emisión") -> SAnime.ONGOING
-            statusString.contains("Finalizado") -> SAnime.COMPLETED
+            text().contains("finalizado", true) -> SAnime.COMPLETED
+            text().contains("emision", true) -> SAnime.ONGOING
             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) {
-            ""
+    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 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"
@@ -287,21 +235,21 @@ class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
                 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)
-                }
+        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()
             }
-        }
-    }
-
-    inline fun <A> Iterable<A>.parallelForEachBlocking(crossinline f: suspend (A) -> Unit) {
-        runBlocking {
-            this@parallelForEachBlocking.parallelForEach(f)
-        }
+        }.also(screen::addPreference)
     }
 }
diff --git a/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/extractors/SolidFilesExtractor.kt b/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/extractors/SolidFilesExtractor.kt
deleted file mode 100644
index a66b611e..00000000
--- a/src/es/animefenix/src/eu/kanade/tachiyomi/animeextension/es/animefenix/extractors/SolidFilesExtractor.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-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
-        }
-    }
-}
diff --git a/src/es/cinecalidad/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/es/cinecalidad/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index a31f7028..00000000
Binary files a/src/es/cinecalidad/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cinecalidad/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/es/cinecalidad/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 3ce663b6..00000000
Binary files a/src/es/cinecalidad/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/cinecalidad/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/es/cinecalidad/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 21d5008b..00000000
Binary files a/src/es/cinecalidad/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cinecalidad/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/es/cinecalidad/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 49b2a43e..00000000
Binary files a/src/es/cinecalidad/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/cinecalidad/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/es/cinecalidad/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 776ce9a3..00000000
Binary files a/src/es/cinecalidad/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cinecalidad/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/es/cinecalidad/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index e18190fe..00000000
Binary files a/src/es/cinecalidad/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/cinecalidad/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/es/cinecalidad/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 9de1ea8f..00000000
Binary files a/src/es/cinecalidad/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cinecalidad/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/es/cinecalidad/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index b65fd053..00000000
Binary files a/src/es/cinecalidad/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/cinecalidad/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/es/cinecalidad/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 81b316ba..00000000
Binary files a/src/es/cinecalidad/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cinecalidad/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/es/cinecalidad/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 13dcfcb4..00000000
Binary files a/src/es/cinecalidad/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/es/cineplus123/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 3f50df92..00000000
Binary files a/src/es/cineplus123/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/es/cineplus123/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index ca607167..00000000
Binary files a/src/es/cineplus123/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/es/cineplus123/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 5a270b5a..00000000
Binary files a/src/es/cineplus123/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/es/cineplus123/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 8efd6289..00000000
Binary files a/src/es/cineplus123/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/es/cineplus123/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 36d4e2d1..00000000
Binary files a/src/es/cineplus123/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/es/cineplus123/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 5091966c..00000000
Binary files a/src/es/cineplus123/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/es/cineplus123/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index aacf215b..00000000
Binary files a/src/es/cineplus123/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/es/cineplus123/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 636d2123..00000000
Binary files a/src/es/cineplus123/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/es/cineplus123/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 50e3aba3..00000000
Binary files a/src/es/cineplus123/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/es/cineplus123/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/es/cineplus123/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 923c8960..00000000
Binary files a/src/es/cineplus123/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/es/latanime/res/web_hi_res_512.png b/src/es/latanime/res/web_hi_res_512.png
deleted file mode 100644
index c67e40f7..00000000
Binary files a/src/es/latanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/es/otakuverso/build.gradle b/src/es/otakuverso/build.gradle
new file mode 100644
index 00000000..830bd251
--- /dev/null
+++ b/src/es/otakuverso/build.gradle
@@ -0,0 +1,27 @@
+ext {
+    extName = 'Otakuverso'
+    extClass = '.Otakuverso'
+    extVersionCode = 1
+}
+
+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:dood-extractor'))
+    implementation(project(':lib:upstream-extractor'))
+    implementation(project(':lib:streamhidevid-extractor'))
+    implementation(project(':lib:universal-extractor'))
+    implementation(project(':lib:sendvid-extractor'))
+    implementation(project(':lib:playlist-utils'))
+    implementation(libs.jsunpacker)
+}
\ No newline at end of file
diff --git a/src/es/otakuverso/res/mipmap-hdpi/ic_launcher.png b/src/es/otakuverso/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..cf3d0126
Binary files /dev/null and b/src/es/otakuverso/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/es/otakuverso/res/mipmap-mdpi/ic_launcher.png b/src/es/otakuverso/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..cf3d0126
Binary files /dev/null and b/src/es/otakuverso/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/es/otakuverso/res/mipmap-xhdpi/ic_launcher.png b/src/es/otakuverso/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..cf3d0126
Binary files /dev/null and b/src/es/otakuverso/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/es/otakuverso/res/mipmap-xxhdpi/ic_launcher.png b/src/es/otakuverso/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..cf3d0126
Binary files /dev/null and b/src/es/otakuverso/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/es/otakuverso/res/mipmap-xxxhdpi/ic_launcher.png b/src/es/otakuverso/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..cf3d0126
Binary files /dev/null and b/src/es/otakuverso/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/es/otakuverso/src/eu/kanade/tachiyomi/animeextension/es/otakuverso/Otakuverso.kt b/src/es/otakuverso/src/eu/kanade/tachiyomi/animeextension/es/otakuverso/Otakuverso.kt
new file mode 100644
index 00000000..025b34a2
--- /dev/null
+++ b/src/es/otakuverso/src/eu/kanade/tachiyomi/animeextension/es/otakuverso/Otakuverso.kt
@@ -0,0 +1,339 @@
+package eu.kanade.tachiyomi.animeextension.es.otakuverso
+
+import android.app.Application
+import android.content.SharedPreferences
+import android.util.Log
+import androidx.preference.ListPreference
+import androidx.preference.PreferenceScreen
+import eu.kanade.tachiyomi.animeextension.es.otakuverso.extractors.UnpackerExtractor
+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.filemoonextractor.FilemoonExtractor
+import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
+import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
+import eu.kanade.tachiyomi.lib.sendvidextractor.SendvidExtractor
+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.network.POST
+import eu.kanade.tachiyomi.network.await
+import eu.kanade.tachiyomi.util.asJsoup
+import eu.kanade.tachiyomi.util.parallelCatchingFlatMap
+import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
+import okhttp3.FormBody
+import okhttp3.HttpUrl.Companion.toHttpUrl
+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 Otakuverso : ConfigurableAnimeSource, AnimeHttpSource() {
+
+    override val name = "Otakuverso"
+
+    override val baseUrl = "https://otakuverso.net"
+
+    override val lang = "es"
+
+    override val supportsLatest = false
+
+    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 = "YourUpload"
+        private val SERVER_LIST = arrayOf(
+            "YourUpload", "Voe", "Mp4Upload", "Doodstream",
+            "Upload", "BurstCloud", "Upstream", "StreamTape",
+            "Fastream", "Filemoon", "StreamWish", "Okru",
+            "Amazon", "AmazonES", "Fireload", "FileLions",
+        )
+    }
+
+    override fun animeDetailsParse(response: Response): SAnime {
+        val document = response.asJsoup()
+        return SAnime.create().apply {
+            title = document.selectFirst("#back_data_perfil .inn-text h1")?.text().orEmpty()
+            description = document.selectFirst("#back_data_perfil .inn-text p.font14")?.ownText()
+            genre = document.select(".pre .text-deco-none.font-GDSherpa-Regular").joinToString { it.text() }
+            status = with(document.select("#back_data_perfil .inn-text .btn-anime-info")) {
+                when {
+                    text().contains("finalizado", true) -> SAnime.COMPLETED
+                    text().contains("emision", true) || text().contains("emitiéndose", true) -> SAnime.ONGOING
+                    else -> SAnime.UNKNOWN
+                }
+            }
+            document.select(".col-xl-12 .font-GDSherpa-Regular")
+                .map { it.select(".text-white").text() to it.select(".lila-color").text() }
+                .forEach { (title, content) ->
+                    when {
+                        title.contains("Creador(a)", true) -> author = content
+                        title.contains("Director(a)", true) -> artist = content
+                    }
+                }
+        }
+    }
+
+    private fun getToken(): Pair<String, String> {
+        try {
+            val request = client.newCall(GET("$baseUrl/animes")).execute()
+            val document = request.asJsoup()
+            val token = document.selectFirst("[name=\"_token\"]")?.attr("value").orEmpty()
+            val xsrfToken = client.cookieJar.loadForRequest("$baseUrl/animes".toHttpUrl())
+                .firstOrNull { it.name == "XSRF-TOKEN" }?.let { "${it.name}=${it.value}" }
+                .orEmpty()
+            return token to xsrfToken
+        } catch (e: Exception) {
+            Log.i("bruh err", e.toString())
+            return "" to ""
+        }
+    }
+
+    override fun popularAnimeRequest(page: Int): Request {
+        val (token, xsrfToken) = getToken()
+        val data = FormBody.Builder()
+            .add("_token", token)
+            .add("page", "$page")
+            .add("search_genero", "0")
+            .add("search_anno", "0")
+            .add("search_tipo", "0")
+            .add("search_orden", "0")
+            .add("search_estado", "0")
+            .add("Cookie", xsrfToken)
+            .build()
+
+        return POST("$baseUrl/animes", body = data, headers = headers)
+    }
+
+    override fun popularAnimeParse(response: Response): AnimesPage {
+        val document = response.asJsoup()
+        val elements = document.select(".row [data-original-title]")
+        val nextPage = document.select(".pagination a[rel=next]").any()
+        val animeList = elements.map { element ->
+            SAnime.create().apply {
+                title = element.selectFirst(".font-GDSherpa-Bold")?.text().orEmpty()
+                thumbnail_url = element.selectFirst("img")?.getImageUrl()
+                setUrlWithoutDomain(element.attr("abs:href"))
+            }
+        }
+        return AnimesPage(animeList, nextPage)
+    }
+
+    override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
+
+    override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
+
+    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
+        val params = OtakuversoFilters.getSearchParameters(filters)
+        return when {
+            query.isNotBlank() -> GET("$baseUrl/buscador?q=$query&page=$page", headers)
+            params.isFiltered() -> searchRequest(params, page)
+            else -> popularAnimeRequest(page)
+        }
+    }
+
+    private fun searchRequest(params: OtakuversoFilters.FilterSearchParams, page: Int): Request {
+        val formBody = params.body
+        val (token, xsrfToken) = getToken()
+        val data = FormBody.Builder().apply {
+            for (i in 0 until formBody.size) {
+                add(formBody.name(i), formBody.value(i))
+            }
+            add("page", "$page")
+            add("_token", token)
+            add("Cookie", xsrfToken)
+        }.build()
+
+        return POST("$baseUrl/animes", body = data, headers = headers)
+    }
+
+    override fun searchAnimeParse(response: Response) = popularAnimeParse(response)
+
+    private fun parseEpisodeList(document: Document): List<SEpisode> {
+        return document.select(".pl-lg-4 .container-fluid .row .col-6.text-center").map {
+            val episode = it.select(".font-GDSherpa-Bold a")
+            val episodeNumber = episode.text().substringAfter("Episodio").trim().toFloat()
+            SEpisode.create().apply {
+                name = episode.text()
+                episode_number = episodeNumber
+                scanlator = it.select(".font14 .bog").text().trim()
+                setUrlWithoutDomain(episode.attr("abs:href"))
+            }
+        }
+    }
+
+    override fun episodeListParse(response: Response): List<SEpisode> {
+        val document = response.asJsoup()
+        val pageable = document.select(".dropdown-menu").any()
+        if (pageable) {
+            return document.select(".dropdown-menu a")
+                .map { it.attr("abs:href") }
+                .parallelCatchingFlatMapBlocking {
+                    val page = client.newCall(GET(it)).execute().asJsoup()
+                    parseEpisodeList(page)
+                }
+        }
+        return parseEpisodeList(document)
+    }
+
+    override suspend fun getVideoList(episode: SEpisode): List<Video> {
+        val response = client.newCall(videoListRequest(episode)).await()
+        val document = response.asJsoup()
+        return document.select("#ssel option")
+            .map { it.attr("value") }
+            .parallelCatchingFlatMap { id ->
+                val url = getRealLink(id)
+                serverVideoResolver(url)
+            }
+    }
+
+    private fun getRealLink(id: String): String {
+        val serverResponse = client.newCall(GET("$baseUrl/play-video?id=$id")).execute()
+        val serverLink = """"url":"([^"]+)""".toRegex()
+            .find(serverResponse.body.string())
+            ?.groupValues?.get(1)
+            ?.replace("\\/", "/")
+            .orEmpty()
+        return when {
+            serverLink.startsWith("http") -> serverLink
+            serverLink.startsWith("//") -> "https:$serverLink"
+            else -> ""
+        }
+    }
+
+    override fun getFilterList(): AnimeFilterList = OtakuversoFilters.FILTER_LIST
+
+    /*-------------------------------- Video extractors ------------------------------------*/
+    private val universalExtractor by lazy { UniversalExtractor(client) }
+    private val voeExtractor by lazy { VoeExtractor(client) }
+    private val okruExtractor by lazy { OkruExtractor(client) }
+    private val filemoonExtractor by lazy { FilemoonExtractor(client) }
+    private val uqloadExtractor by lazy { UqloadExtractor(client) }
+    private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
+    private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
+    private val doodExtractor by lazy { DoodExtractor(client) }
+    private val streamlareExtractor by lazy { StreamlareExtractor(client) }
+    private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
+    private val burstcloudExtractor by lazy { BurstCloudExtractor(client) }
+    private val upstreamExtractor by lazy { UpstreamExtractor(client) }
+    private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
+    private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client, headers) }
+    private val filelionsExtractor by lazy { StreamWishExtractor(client, headers) }
+    private val sendvidExtractor by lazy { SendvidExtractor(client, headers) }
+    private val luluExtractor by lazy { UnpackerExtractor(client, headers) }
+
+    private fun serverVideoResolver(url: String): List<Video> {
+        return when {
+            arrayOf("voe", "robertordercharacter", "donaldlineelse").any(url) -> voeExtractor.videosFromUrl(url)
+            arrayOf("ok.ru", "okru").any(url) -> okruExtractor.videosFromUrl(url)
+            arrayOf("moon").any(url) -> filemoonExtractor.videosFromUrl(url, prefix = "Filemoon:")
+            arrayOf("uqload").any(url) -> uqloadExtractor.videosFromUrl(url)
+            arrayOf("mp4upload").any(url) -> mp4uploadExtractor.videosFromUrl(url, headers)
+            arrayOf("wish").any(url) -> streamwishExtractor.videosFromUrl(url, videoNameGen = { "StreamWish:$it" })
+            arrayOf("doodstream", "dood.").any(url) -> doodExtractor.videosFromUrl(url, "DoodStream")
+            arrayOf("streamlare").any(url) -> streamlareExtractor.videosFromUrl(url)
+            arrayOf("yourupload", "upload").any(url) -> yourUploadExtractor.videoFromUrl(url, headers = headers)
+            arrayOf("burstcloud", "burst").any(url) -> burstcloudExtractor.videoFromUrl(url, headers = headers)
+            arrayOf("upstream").any(url) -> upstreamExtractor.videosFromUrl(url)
+            arrayOf("streamtape", "stp", "stape").any(url) -> streamTapeExtractor.videosFromUrl(url)
+            arrayOf("ahvsh", "streamhide").any(url) -> streamHideVidExtractor.videosFromUrl(url)
+            arrayOf("/stream/fl.php").any(url) -> {
+                val video = url.substringAfter("/stream/fl.php?v=")
+                if (client.newCall(GET(video)).execute().code == 200) {
+                    listOf(Video(video, "FireLoad", video))
+                } else {
+                    emptyList()
+                }
+            }
+            arrayOf("lion").any(url) -> filelionsExtractor.videosFromUrl(url, videoNameGen = { "FileLions:$it" })
+            arrayOf("sendvid").any(url) -> sendvidExtractor.videosFromUrl(url)
+            arrayOf("lulu").any(url) -> luluExtractor.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()
+    }
+
+    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/")
+    }
+
+    private fun Array<String>.any(url: String): Boolean = this.any { url.contains(it, ignoreCase = true) }
+
+    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/otakuverso/src/eu/kanade/tachiyomi/animeextension/es/otakuverso/OtakuversoFilters.kt b/src/es/otakuverso/src/eu/kanade/tachiyomi/animeextension/es/otakuverso/OtakuversoFilters.kt
new file mode 100644
index 00000000..073c4488
--- /dev/null
+++ b/src/es/otakuverso/src/eu/kanade/tachiyomi/animeextension/es/otakuverso/OtakuversoFilters.kt
@@ -0,0 +1,143 @@
+package eu.kanade.tachiyomi.animeextension.es.otakuverso
+
+import eu.kanade.tachiyomi.animesource.model.AnimeFilter
+import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
+import okhttp3.FormBody
+import java.util.Calendar
+
+object OtakuversoFilters {
+    open class QueryPartFilter(displayName: String, val vals: Array<Pair<String, String>>) : AnimeFilter.Select<String>(
+        displayName,
+        vals.map { it.first }.toTypedArray(),
+    )
+
+    private inline fun <reified R> AnimeFilterList.getFirst(): R {
+        return this.filterIsInstance<R>().first()
+    }
+
+    data class FilterSearchParams(
+        val body: FormBody,
+    ) {
+        fun isFiltered(): Boolean {
+            return (0 until body.size).any { body.value(it) != "0" }
+        }
+    }
+
+    internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
+        if (filters.isEmpty()) return FilterSearchParams(FormBody.Builder().build())
+
+        val formBuilder = FormBody.Builder()
+        filters.getFirst<GenresFilter>().let { filter ->
+            formBuilder.add("search_genero", filter.vals[filter.state].second)
+        }
+        filters.getFirst<TypesFilter>().let { filter ->
+            formBuilder.add("search_tipo", filter.vals[filter.state].second)
+        }
+        filters.getFirst<StatusFilter>().let { filter ->
+            formBuilder.add("search_estado", filter.vals[filter.state].second)
+        }
+        filters.getFirst<YearsFilter>().let { filter ->
+            formBuilder.add("search_anno", filter.vals[filter.state].second)
+        }
+        filters.getFirst<SortFilter>().let { filter ->
+            formBuilder.add("search_orden", filter.vals[filter.state].second)
+        }
+        return FilterSearchParams(formBuilder.build())
+    }
+
+    val FILTER_LIST get() = AnimeFilterList(
+        AnimeFilter.Header("La busqueda por texto ignora el filtro"),
+        GenresFilter(),
+        TypesFilter(),
+        StatusFilter(),
+        YearsFilter(),
+        SortFilter(),
+    )
+
+    class GenresFilter : QueryPartFilter("Género", OtakuversoFiltersData.GENRES)
+
+    class TypesFilter : QueryPartFilter("Tipo", OtakuversoFiltersData.TYPES)
+
+    class StatusFilter : QueryPartFilter("Estado", OtakuversoFiltersData.STATUS)
+
+    class YearsFilter : QueryPartFilter("Año", OtakuversoFiltersData.YEARS)
+
+    class SortFilter : QueryPartFilter("Orden", OtakuversoFiltersData.SORT)
+
+    private object OtakuversoFiltersData {
+        val GENRES = arrayOf(
+            Pair("Todos", "0"),
+            Pair("Aventura", "jR"),
+            Pair("Misterio", "k5"),
+            Pair("Shounen", "l5"),
+            Pair("Acción", "mO"),
+            Pair("Fantasía", "nR"),
+            Pair("Demonios", "oj"),
+            Pair("Histórico", "p2"),
+            Pair("Sobrenatural", "q2"),
+            Pair("Artes Marciales", "rE"),
+            Pair("Comedia", "vm"),
+            Pair("Superpoderes", "wR"),
+            Pair("Magia", "x9"),
+            Pair("Deportes", "y7"),
+            Pair("Drama", "zY"),
+            Pair("Escolares", "AO"),
+            Pair("Ciencia Ficción", "BX"),
+            Pair("Horror", "Dx"),
+            Pair("Psicológico", "Ev"),
+            Pair("Juegos", "G7"),
+            Pair("Romance", "J2"),
+            Pair("Seinen", "KR"),
+            Pair("Recuentos de la vida", "Lw"),
+            Pair("Mecha", "MA"),
+            Pair("Shoujo", "N6"),
+            Pair("Policía", "Op"),
+            Pair("Suspenso", "Pw"),
+            Pair("Música", "Ql"),
+            Pair("Parodia", "Rq"),
+            Pair("Ecchi", "VM"),
+            Pair("Terror", "WJ"),
+            Pair("Militar", "XW"),
+            Pair("Vampiros", "YK"),
+            Pair("Samurai", "ZJ"),
+            Pair("Infantil", "1R"),
+            Pair("Harem", "2K"),
+            Pair("Escuela", "3M"),
+            Pair("Carreras", "41"),
+            Pair("Lucha", "5B"),
+            Pair("Gore", "6n"),
+            Pair("Latino", "7j"),
+            Pair("Fútbol", "8m"),
+            Pair("Espacial", "9x"),
+            Pair("Josei", "0v"),
+            Pair("Comida", "gJY"),
+            Pair("School", "jRR"),
+            Pair("Yuri", "kR5"),
+            Pair("Yaoi", "lY5"),
+            Pair("Shounen Ai", "mZO"),
+        )
+
+        val TYPES = arrayOf(
+            Pair("Todos", "0"),
+            Pair("Serie", "1"),
+            Pair("Película", "2"),
+            Pair("Especial", "3"),
+            Pair("OVA", "4"),
+        )
+
+        val STATUS = arrayOf(
+            Pair("Todos", "0"),
+            Pair("Emitiendose", "1"),
+            Pair("Próximo", "2"),
+            Pair("Finalizado", "3"),
+        )
+
+        val YEARS = arrayOf(Pair("Todos", "0")) + (1980..Calendar.getInstance().get(Calendar.YEAR)).map { Pair("$it", "$it") }.reversed().toTypedArray()
+
+        val SORT = arrayOf(
+            Pair("Default", "0"),
+            Pair("Ascendente", "1"),
+            Pair("Descendente", "2"),
+        )
+    }
+}
diff --git a/src/es/otakuverso/src/eu/kanade/tachiyomi/animeextension/es/otakuverso/extractors/UnpackerExtractor.kt b/src/es/otakuverso/src/eu/kanade/tachiyomi/animeextension/es/otakuverso/extractors/UnpackerExtractor.kt
new file mode 100644
index 00000000..5555e497
--- /dev/null
+++ b/src/es/otakuverso/src/eu/kanade/tachiyomi/animeextension/es/otakuverso/extractors/UnpackerExtractor.kt
@@ -0,0 +1,31 @@
+package eu.kanade.tachiyomi.animeextension.es.otakuverso.extractors
+
+import dev.datlag.jsunpacker.JsUnpacker
+import eu.kanade.tachiyomi.animesource.model.Video
+import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.util.asJsoup
+import okhttp3.Headers
+import okhttp3.OkHttpClient
+
+class UnpackerExtractor(private val client: OkHttpClient, private val headers: Headers) {
+    private val playlistUtils by lazy { PlaylistUtils(client, headers) }
+
+    fun videosFromUrl(url: String): List<Video> {
+        val doc = client.newCall(GET(url, headers)).execute()
+            .asJsoup()
+
+        val script = doc.selectFirst("script:containsData(eval)")
+            ?.data()
+            ?.let(JsUnpacker::unpackAndCombine)
+            ?: return emptyList()
+
+        val playlistUrl = script.substringAfter("file:\"").substringBefore('"')
+
+        return playlistUtils.extractFromHls(
+            playlistUrl,
+            referer = playlistUrl,
+            videoNameGen = { "Lulu:$it" },
+        )
+    }
+}
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/fr/animesama/res/web_hi_res_512.png b/src/fr/animesama/res/web_hi_res_512.png
deleted file mode 100644
index ebaf597e..00000000
Binary files a/src/fr/animesama/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/src/fr/empirestreaming/res/mipmap-hdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 8dc560dd..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/src/fr/empirestreaming/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 6b3ec01b..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/src/fr/empirestreaming/res/mipmap-mdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index f8feb6e5..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/src/fr/empirestreaming/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 4af4a29a..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/src/fr/empirestreaming/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 1b560124..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/src/fr/empirestreaming/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 93321f34..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/src/fr/empirestreaming/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index 369383ac..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/src/fr/empirestreaming/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 06a62196..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/src/fr/empirestreaming/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
deleted file mode 100644
index de26e98f..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ
diff --git a/src/fr/empirestreaming/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/src/fr/empirestreaming/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
deleted file mode 100644
index 7cad125f..00000000
Binary files a/src/fr/empirestreaming/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ
diff --git a/src/fr/frenchanime/res/web_hi_res_512.png b/src/fr/frenchanime/res/web_hi_res_512.png
deleted file mode 100644
index 0089d6ef..00000000
Binary files a/src/fr/frenchanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/fr/jetanime/res/web_hi_res_512.png b/src/fr/jetanime/res/web_hi_res_512.png
deleted file mode 100644
index b219ab1b..00000000
Binary files a/src/fr/jetanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/fr/mykdrama/res/web_hi_res_512.png b/src/fr/mykdrama/res/web_hi_res_512.png
deleted file mode 100644
index 579972f1..00000000
Binary files a/src/fr/mykdrama/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/fr/otakufr/res/web_hi_res_512.png b/src/fr/otakufr/res/web_hi_res_512.png
deleted file mode 100644
index d992179a..00000000
Binary files a/src/fr/otakufr/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/fr/wiflix/res/web_hi_res_512.png b/src/fr/wiflix/res/web_hi_res_512.png
deleted file mode 100644
index 1aacf919..00000000
Binary files a/src/fr/wiflix/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/hi/animesaga/build.gradle b/src/hi/animesaga/build.gradle
index 1da6278d..bcfeeaa0 100644
--- a/src/hi/animesaga/build.gradle
+++ b/src/hi/animesaga/build.gradle
@@ -3,7 +3,7 @@ ext {
     extClass = '.AniSAGA'
     themePkg = 'dooplay'
     baseUrl = 'https://www.anisaga.org'
-    overrideVersionCode = 15
+    overrideVersionCode = 16
     isNsfw = false
 }
 
diff --git a/src/hi/animesaga/src/eu/kanade/tachiyomi/animeextension/hi/animesaga/AniSAGA.kt b/src/hi/animesaga/src/eu/kanade/tachiyomi/animeextension/hi/animesaga/AniSAGA.kt
index c9e15245..6c077aed 100644
--- a/src/hi/animesaga/src/eu/kanade/tachiyomi/animeextension/hi/animesaga/AniSAGA.kt
+++ b/src/hi/animesaga/src/eu/kanade/tachiyomi/animeextension/hi/animesaga/AniSAGA.kt
@@ -14,7 +14,7 @@ class AniSAGA : DooPlay(
     "AniSAGA",
     "https://www.anisaga.org",
 ) {
-    private val videoHost = "https://cdn.anisaga.org"
+    private val videoHost = "https://plyrxcdn.site/"
 
     // ============================== Popular ===============================
     override fun popularAnimeSelector() = "div.top-imdb-list > div.top-imdb-item"
diff --git a/src/hi/yomovies/res/web_hi_res_512.png b/src/hi/yomovies/res/web_hi_res_512.png
deleted file mode 100644
index d8cce433..00000000
Binary files a/src/hi/yomovies/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/id/kuramanime/res/web_hi_res_512.png b/src/id/kuramanime/res/web_hi_res_512.png
deleted file mode 100644
index 8f1628be..00000000
Binary files a/src/id/kuramanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/id/kuronime/res/web_hi_res_512.png b/src/id/kuronime/res/web_hi_res_512.png
deleted file mode 100644
index a469dc18..00000000
Binary files a/src/id/kuronime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/id/minioppai/res/web_hi_res_512.png b/src/id/minioppai/res/web_hi_res_512.png
deleted file mode 100644
index 06ca48d3..00000000
Binary files a/src/id/minioppai/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/id/neonime/res/web_hi_res_128.png b/src/id/neonime/res/web_hi_res_128.png
deleted file mode 100644
index 170b0247..00000000
Binary files a/src/id/neonime/res/web_hi_res_128.png and /dev/null differ
diff --git a/src/id/oploverz/res/web_hi_res_512.png b/src/id/oploverz/res/web_hi_res_512.png
deleted file mode 100644
index f6e7473a..00000000
Binary files a/src/id/oploverz/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/id/otakudesu/res/web_hi_res_512.png b/src/id/otakudesu/res/web_hi_res_512.png
deleted file mode 100644
index fe706242..00000000
Binary files a/src/id/otakudesu/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/id/samehadaku/res/web_hi_res_512.png b/src/id/samehadaku/res/web_hi_res_512.png
deleted file mode 100644
index b8a3e6a8..00000000
Binary files a/src/id/samehadaku/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/it/animeunity/res/web_hi_res_512.png b/src/it/animeunity/res/web_hi_res_512.png
deleted file mode 100644
index 005a0666..00000000
Binary files a/src/it/animeunity/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/it/animeworld/res/play_store_512.png b/src/it/animeworld/res/play_store_512.png
deleted file mode 100644
index 470fb01b..00000000
Binary files a/src/it/animeworld/res/play_store_512.png and /dev/null differ
diff --git a/src/it/streamingcommunity/res/web_hi_res_512.png b/src/it/streamingcommunity/res/web_hi_res_512.png
deleted file mode 100644
index bb51a179..00000000
Binary files a/src/it/streamingcommunity/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/it/toonitalia/res/web_hi_res_512.png b/src/it/toonitalia/res/web_hi_res_512.png
deleted file mode 100644
index bb20f995..00000000
Binary files a/src/it/toonitalia/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/it/vvvvid/res/web_hi_res_512.png b/src/it/vvvvid/res/web_hi_res_512.png
deleted file mode 100644
index 2b0c990f..00000000
Binary files a/src/it/vvvvid/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/ko/aniweek/res/web_hi_res_512.png b/src/ko/aniweek/res/web_hi_res_512.png
deleted file mode 100644
index c4fb09a4..00000000
Binary files a/src/ko/aniweek/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/pl/desuonline/res/web_hi_res_512.png b/src/pl/desuonline/res/web_hi_res_512.png
deleted file mode 100644
index 641868dd..00000000
Binary files a/src/pl/desuonline/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/pl/wbijam/res/web_hi_res_512.png b/src/pl/wbijam/res/web_hi_res_512.png
deleted file mode 100644
index 6da98071..00000000
Binary files a/src/pl/wbijam/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/pt/anidong/AndroidManifest.xml b/src/pt/anidong/AndroidManifest.xml
deleted file mode 100644
index 2970f0d2..00000000
--- a/src/pt/anidong/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <application>
-        <activity
-            android:name=".pt.anidong.AniDongUrlActivity"
-            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="anidong.net"
-                    android:pathPattern="/anime/..*"
-                    android:scheme="https" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/src/pt/anidong/build.gradle b/src/pt/anidong/build.gradle
deleted file mode 100644
index 57fb4d05..00000000
--- a/src/pt/anidong/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-ext {
-    extName = 'AniDong'
-    extClass = '.AniDong'
-    extVersionCode = 2
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/pt/anidong/res/mipmap-hdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 50be877d..00000000
Binary files a/src/pt/anidong/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/anidong/res/mipmap-mdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index b23fff3a..00000000
Binary files a/src/pt/anidong/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/anidong/res/mipmap-xhdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 71ddb7b8..00000000
Binary files a/src/pt/anidong/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/anidong/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 720b33eb..00000000
Binary files a/src/pt/anidong/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/anidong/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index a0cb538f..00000000
Binary files a/src/pt/anidong/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt b/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt
deleted file mode 100644
index ea22f1d3..00000000
--- a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt
+++ /dev/null
@@ -1,264 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.anidong
-
-import eu.kanade.tachiyomi.animeextension.pt.anidong.dto.EpisodeDto
-import eu.kanade.tachiyomi.animeextension.pt.anidong.dto.EpisodeListDto
-import eu.kanade.tachiyomi.animeextension.pt.anidong.dto.SearchResultDto
-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.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.network.awaitSuccess
-import eu.kanade.tachiyomi.util.asJsoup
-import kotlinx.serialization.json.Json
-import okhttp3.FormBody
-import okhttp3.Request
-import okhttp3.Response
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-import uy.kohesive.injekt.injectLazy
-
-class AniDong : ParsedAnimeHttpSource() {
-
-    override val name = "AniDong"
-
-    override val baseUrl = "https://anidong.net"
-
-    override val lang = "pt-BR"
-
-    override val supportsLatest = true
-
-    private val json: Json by injectLazy()
-
-    private val apiHeaders by lazy {
-        headersBuilder() // sets user-agent
-            .add("Referer", baseUrl)
-            .add("x-requested-with", "XMLHttpRequest")
-            .build()
-    }
-
-    // ============================== Popular ===============================
-    override fun popularAnimeRequest(page: Int) = GET(baseUrl)
-
-    override fun popularAnimeSelector() = "article.top10_animes_item > a"
-
-    override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
-        setUrlWithoutDomain(element.attr("href"))
-        title = element.attr("title")
-        thumbnail_url = element.selectFirst("img")?.attr("src")
-    }
-
-    override fun popularAnimeNextPageSelector() = null
-
-    // =============================== Latest ===============================
-    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/lancamentos/page/$page/")
-
-    override fun latestUpdatesSelector() = "article.main_content_article > a"
-
-    override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
-
-    override fun latestUpdatesNextPageSelector() = "div.paginacao > a.next"
-
-    // =============================== 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/anime/$id"))
-                .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 getFilterList() = AniDongFilters.FILTER_LIST
-
-    private val nonce by lazy {
-        client.newCall(GET("$baseUrl/?js_global=1&ver=6.2.2")).execute()
-            .body.string()
-            .substringAfter("search_nonce")
-            .substringAfter("'")
-            .substringBefore("'")
-    }
-
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
-        val params = AniDongFilters.getSearchParameters(filters)
-
-        val body = FormBody.Builder()
-            .add("letra", "")
-            .add("action", "show_animes_ajax")
-            .add("nome", query)
-            .add("status", params.status)
-            .add("formato", params.format)
-            .add("search_nonce", nonce)
-            .add("paged", page.toString())
-            .apply {
-                params.genres.forEach { add("generos[]", it) }
-            }.build()
-
-        return POST("$baseUrl/wp-admin/admin-ajax.php", headers = apiHeaders, body = body)
-    }
-
-    override fun searchAnimeParse(response: Response): AnimesPage {
-        val searchData: SearchResultDto = response.body.string()
-            .takeIf { it.trim() != "402" }
-            ?.let(json::decodeFromString)
-            ?: return AnimesPage(emptyList(), false)
-
-        val animes = searchData.animes.map {
-            SAnime.create().apply {
-                setUrlWithoutDomain(it.url)
-                title = it.title
-                thumbnail_url = it.thumbnail_url
-            }
-        }
-
-        val hasNextPage = searchData.pages > 1 && searchData.animes.size == 10
-
-        return AnimesPage(animes, hasNextPage)
-    }
-
-    override fun searchAnimeSelector(): String {
-        throw UnsupportedOperationException()
-    }
-
-    override fun searchAnimeFromElement(element: Element): SAnime {
-        throw UnsupportedOperationException()
-    }
-
-    override fun searchAnimeNextPageSelector(): String? {
-        throw UnsupportedOperationException()
-    }
-
-    // =========================== Anime Details ============================
-    override fun animeDetailsParse(document: Document) = SAnime.create().apply {
-        val doc = getRealDoc(document)
-        val infos = doc.selectFirst("div.anime_infos")!!
-
-        setUrlWithoutDomain(doc.location())
-        title = infos.selectFirst("div > h3")!!.ownText()
-        thumbnail_url = infos.selectFirst("img")?.attr("src")
-        genre = infos.select("div[itemprop=genre] a").eachText().joinToString()
-        artist = infos.selectFirst("div[itemprop=productionCompany]")?.text()
-
-        status = doc.selectFirst("div:contains(Status) span")?.text().let {
-            when {
-                it == null -> SAnime.UNKNOWN
-                it == "Completo" -> SAnime.COMPLETED
-                it.contains("Lançamento") -> SAnime.ONGOING
-                else -> SAnime.UNKNOWN
-            }
-        }
-
-        description = buildString {
-            infos.selectFirst("div.anime_name + div.anime_info")?.text()?.also {
-                append("Nomes alternativos: $it\n")
-            }
-
-            doc.selectFirst("div[itemprop=description]")?.text()?.also {
-                append("\n$it")
-            }
-        }
-    }
-
-    // ============================== Episodes ==============================
-    override fun episodeListSelector(): String {
-        throw UnsupportedOperationException()
-    }
-
-    override fun episodeFromElement(element: Element): SEpisode {
-        throw UnsupportedOperationException()
-    }
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val doc = getRealDoc(response.asJsoup())
-
-        val id = doc.selectFirst("link[rel=shortlink]")!!.attr("href").substringAfter("=")
-        val body = FormBody.Builder()
-            .add("action", "show_videos")
-            .add("anime_id", id)
-            .build()
-
-        val res = client.newCall(POST("$baseUrl/api", apiHeaders, body)).execute()
-            .body.string()
-        val data = json.decodeFromString<EpisodeListDto>(res)
-
-        return buildList {
-            data.episodes.forEach { add(episodeFromObject(it, "Episódio")) }
-            data.movies.forEach { add(episodeFromObject(it, "Filme")) }
-            data.ovas.forEach { add(episodeFromObject(it, "OVA")) }
-            sortByDescending { it.episode_number }
-        }
-    }
-
-    private fun episodeFromObject(episode: EpisodeDto, prefix: String) = SEpisode.create().apply {
-        setUrlWithoutDomain(episode.epi_url)
-        episode_number = episode.epi_num.toFloatOrNull() ?: 0F
-        name = "$prefix ${episode.epi_num}"
-    }
-
-    // ============================ Video Links =============================
-    override fun videoListParse(response: Response): List<Video> {
-        val doc = response.asJsoup()
-        return doc.select("div.player_option").flatMap {
-            val url = it.attr("data-playerlink")
-            val playerName = it.text().trim()
-            videosFromUrl(url, playerName)
-        }
-    }
-
-    private fun videosFromUrl(url: String, playerName: String): List<Video> {
-        val scriptData = client.newCall(GET(url, apiHeaders)).execute()
-            .asJsoup()
-            .selectFirst("script:containsData(sources)")
-            ?.data() ?: return emptyList()
-
-        return scriptData.substringAfter("sources: [").substringBefore("]")
-            .split("{")
-            .drop(1)
-            .map {
-                val videoUrl = it.substringAfter("file: \"").substringBefore('"')
-                val label = it.substringAfter("label: \"", "Unknown").substringBefore('"')
-                val quality = "$playerName - $label"
-                Video(videoUrl, quality, videoUrl, headers = apiHeaders)
-            }
-    }
-
-    override fun videoFromElement(element: Element): Video {
-        throw UnsupportedOperationException()
-    }
-
-    override fun videoListSelector(): String {
-        throw UnsupportedOperationException()
-    }
-
-    override fun videoUrlParse(document: Document): String {
-        throw UnsupportedOperationException()
-    }
-
-    // ============================= Utilities ==============================
-    private fun getRealDoc(document: Document): Document {
-        if (!document.location().contains("/video/")) return document
-
-        return document.selectFirst(".episodioControleItem:has(i.ri-grid-fill)")?.let {
-            client.newCall(GET(it.attr("href"), headers)).execute()
-                .asJsoup()
-        } ?: document
-    }
-
-    companion object {
-        const val PREFIX_SEARCH = "id:"
-    }
-}
diff --git a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDongFilters.kt b/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDongFilters.kt
deleted file mode 100644
index 28f949e8..00000000
--- a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDongFilters.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.anidong
-
-import eu.kanade.tachiyomi.animesource.model.AnimeFilter
-import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
-
-object AniDongFilters {
-    open class QueryPartFilter(
-        displayName: String,
-        val vals: Array<Pair<String, String>>,
-    ) : AnimeFilter.Select<String>(
-        displayName,
-        vals.map { it.first }.toTypedArray(),
-    ) {
-        fun toQueryPart() = vals[state].second
-    }
-
-    open class CheckBoxFilterList(name: String, val pairs: Array<Pair<String, String>>) :
-        AnimeFilter.Group<AnimeFilter.CheckBox>(name, pairs.map { CheckBoxVal(it.first, false) })
-
-    private class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state)
-
-    private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
-        return (getFirst<R>() as QueryPartFilter).toQueryPart()
-    }
-
-    private inline fun <reified R> AnimeFilterList.getFirst(): R {
-        return first { it is R } as R
-    }
-
-    private inline fun <reified R> AnimeFilterList.parseCheckbox(
-        options: Array<Pair<String, String>>,
-    ): List<String> {
-        return (getFirst<R>() as CheckBoxFilterList).state
-            .asSequence()
-            .filter { it.state }
-            .map { checkbox -> options.find { it.first == checkbox.name }!!.second }
-            .filter(String::isNotBlank)
-            .toList()
-    }
-
-    class StatusFilter : QueryPartFilter("Status", AniDongFiltersData.STATUS_LIST)
-    class FormatFilter : QueryPartFilter("Formato", AniDongFiltersData.FORMAT_LIST)
-
-    class GenresFilter : CheckBoxFilterList("Gêneros", AniDongFiltersData.GENRES_LIST)
-
-    val FILTER_LIST get() = AnimeFilterList(
-        StatusFilter(),
-        FormatFilter(),
-        GenresFilter(),
-    )
-
-    data class FilterSearchParams(
-        val status: String = "",
-        val format: String = "",
-        val genres: List<String> = emptyList(),
-    )
-
-    internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
-        if (filters.isEmpty()) return FilterSearchParams()
-
-        return FilterSearchParams(
-            filters.asQueryPart<StatusFilter>(),
-            filters.asQueryPart<FormatFilter>(),
-            filters.parseCheckbox<GenresFilter>(AniDongFiltersData.GENRES_LIST),
-        )
-    }
-
-    private object AniDongFiltersData {
-        private val SELECT = Pair("<Selecione>", "")
-
-        val STATUS_LIST = arrayOf(
-            SELECT,
-            Pair("Lançamento", "Lançamento"),
-            Pair("Completo", "Completo"),
-        )
-
-        val FORMAT_LIST = arrayOf(
-            SELECT,
-            Pair("Donghua", "Anime"),
-            Pair("Filme", "Filme"),
-        )
-
-        val GENRES_LIST = arrayOf(
-            Pair("Artes Marciais", "9"),
-            Pair("Aventura", "6"),
-            Pair("Ação", "2"),
-            Pair("Boys Love", "43"),
-            Pair("Comédia", "15"),
-            Pair("Corrida", "94"),
-            Pair("Cultivo", "12"),
-            Pair("Demônios", "18"),
-            Pair("Detetive", "24"),
-            Pair("Drama", "16"),
-            Pair("Escolar", "77"),
-            Pair("Espaço", "54"),
-            Pair("Esporte", "95"),
-            Pair("Fantasia", "7"),
-            Pair("Guerra", "26"),
-            Pair("Harém", "17"),
-            Pair("Histórico", "8"),
-            Pair("Horror", "44"),
-            Pair("Isekai", "72"),
-            Pair("Jogo", "25"),
-            Pair("Mecha", "40"),
-            Pair("Militar", "21"),
-            Pair("Mistério", "3"),
-            Pair("Mitolgia", "96"),
-            Pair("Mitologia", "19"),
-            Pair("O Melhor Donghua", "91"),
-            Pair("Polícia", "57"),
-            Pair("Política", "63"),
-            Pair("Psicológico", "33"),
-            Pair("Reencarnação", "30"),
-            Pair("Romance", "11"),
-            Pair("Sci-Fi", "39"),
-            Pair("Slice of Life", "84"),
-            Pair("Sobrenatural", "4"),
-            Pair("Super Poder", "67"),
-            Pair("Suspense", "32"),
-            Pair("Tragédia", "58"),
-            Pair("Vampiro", "82"),
-        )
-    }
-}
diff --git a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDongUrlActivity.kt b/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDongUrlActivity.kt
deleted file mode 100644
index 883d9a50..00000000
--- a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDongUrlActivity.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.anidong
-
-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://anidong.net/anime/<item> intents
- * and redirects them to the main Aniyomi process.
- */
-class AniDongUrlActivity : 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", "${AniDong.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)
-    }
-}
diff --git a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/dto/AniDongDto.kt b/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/dto/AniDongDto.kt
deleted file mode 100644
index 4945071c..00000000
--- a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/dto/AniDongDto.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.anidong.dto
-
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.builtins.ListSerializer
-import kotlinx.serialization.json.JsonArray
-import kotlinx.serialization.json.JsonElement
-import kotlinx.serialization.json.JsonObject
-import kotlinx.serialization.json.JsonTransformingSerializer
-
-@Serializable
-data class SearchResultDto(
-    val animes: List<AnimeDto>,
-    @SerialName("total_pages")
-    val pages: Int,
-)
-
-@Serializable
-data class AnimeDto(
-    @SerialName("anime_capa")
-    val thumbnail_url: String,
-    @SerialName("anime_permalink")
-    val url: String,
-    @SerialName("anime_title")
-    val title: String,
-)
-
-@Serializable
-data class EpisodeListDto(
-    @Serializable(with = EpisodeListSerializer::class)
-    @SerialName("episodios")
-    val episodes: List<EpisodeDto>,
-    @Serializable(with = EpisodeListSerializer::class)
-    @SerialName("filmes")
-    val movies: List<EpisodeDto>,
-    @Serializable(with = EpisodeListSerializer::class)
-    val ovas: List<EpisodeDto>,
-)
-
-@Serializable
-data class EpisodeDto(
-    val epi_num: String,
-    val epi_url: String,
-)
-
-object EpisodeListSerializer :
-    JsonTransformingSerializer<List<EpisodeDto>>(ListSerializer(EpisodeDto.serializer())) {
-    override fun transformDeserialize(element: JsonElement): JsonElement =
-        when (element) {
-            is JsonObject -> JsonArray(element.values.toList())
-            else -> JsonArray(emptyList())
-        }
-}
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/pt/animestc/AndroidManifest.xml b/src/pt/animestc/AndroidManifest.xml
deleted file mode 100644
index e826c520..00000000
--- a/src/pt/animestc/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <application>
-        <activity
-            android:name=".pt.animestc.AnimesTCUrlActivity"
-            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.animestc.net"
-                    android:pathPattern="/animes/..*"
-                    android:scheme="https" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/src/pt/animestc/build.gradle b/src/pt/animestc/build.gradle
deleted file mode 100644
index 50b73a68..00000000
--- a/src/pt/animestc/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-ext {
-    extName = 'AnimesTC'
-    extClass = '.AnimesTC'
-    extVersionCode = 7
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(":lib:googledrive-extractor"))
-}
diff --git a/src/pt/animestc/res/mipmap-hdpi/ic_launcher.png b/src/pt/animestc/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 6d456791..00000000
Binary files a/src/pt/animestc/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animestc/res/mipmap-mdpi/ic_launcher.png b/src/pt/animestc/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 384d412d..00000000
Binary files a/src/pt/animestc/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animestc/res/mipmap-xhdpi/ic_launcher.png b/src/pt/animestc/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 5129c6e5..00000000
Binary files a/src/pt/animestc/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animestc/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/animestc/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index bfbb5520..00000000
Binary files a/src/pt/animestc/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animestc/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/animestc/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index f5229e6a..00000000
Binary files a/src/pt/animestc/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/ATCFilters.kt b/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/ATCFilters.kt
deleted file mode 100644
index 2f830ed1..00000000
--- a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/ATCFilters.kt
+++ /dev/null
@@ -1,174 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animestc
-
-import eu.kanade.tachiyomi.animesource.model.AnimeFilter
-import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
-
-object ATCFilters {
-    open class QueryPartFilter(
-        displayName: String,
-        val vals: Array<Pair<String, String>>,
-    ) : AnimeFilter.Select<String>(
-        displayName,
-        vals.map { it.first }.toTypedArray(),
-    ) {
-        fun toQueryPart() = vals[state].second
-    }
-
-    private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
-        return (first { it is R } as QueryPartFilter).toQueryPart()
-    }
-
-    class TypeFilter : QueryPartFilter("Tipo", ATCFiltersData.TYPES)
-    class YearFilter : QueryPartFilter("Ano", ATCFiltersData.YEARS)
-    class GenreFilter : QueryPartFilter("Gênero", ATCFiltersData.GENRES)
-    class StatusFilter : QueryPartFilter("Status", ATCFiltersData.STATUS)
-
-    val FILTER_LIST get() = AnimeFilterList(
-        TypeFilter(),
-        YearFilter(),
-        GenreFilter(),
-        StatusFilter(),
-    )
-
-    data class FilterSearchParams(
-        val type: String = "series",
-        val year: String = "",
-        val genre: String = "",
-        val status: String = "",
-    )
-
-    internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
-        if (filters.isEmpty()) return FilterSearchParams()
-
-        return FilterSearchParams(
-            filters.asQueryPart<TypeFilter>(),
-            filters.asQueryPart<YearFilter>(),
-            filters.asQueryPart<GenreFilter>(),
-            filters.asQueryPart<StatusFilter>(),
-        )
-    }
-
-    private object ATCFiltersData {
-        val TYPES = arrayOf(
-            Pair("Anime", "series"),
-            Pair("Filme", "movie"),
-            Pair("OVA", "ova"),
-        )
-
-        val SELECT = Pair("Selecione", "")
-
-        val STATUS = arrayOf(
-            SELECT,
-            Pair("Cancelado", "canceled"),
-            Pair("Completo", "complete"),
-            Pair("Em Lançamento", "airing"),
-            Pair("Pausado", "onhold"),
-        )
-
-        val YEARS = arrayOf(SELECT) + (1997..2024).map {
-            Pair(it.toString(), it.toString())
-        }.toTypedArray()
-
-        val GENRES = arrayOf(
-            SELECT,
-            Pair("Ação", "acao"),
-            Pair("Action", "action"),
-            Pair("Adventure", "adventure"),
-            Pair("Artes Marciais", "artes-marciais"),
-            Pair("Artes Marcial", "artes-marcial"),
-            Pair("Aventura", "aventura"),
-            Pair("Beisebol", "beisebol"),
-            Pair("Boys Love", "boys-love"),
-            Pair("Comédia", "comedia"),
-            Pair("Comédia Romântica", "comedia-romantica"),
-            Pair("Comedy", "comedy"),
-            Pair("Crianças", "criancas"),
-            Pair("Culinária", "culinaria"),
-            Pair("Cyberpunk", "cyberpunk"),
-            Pair("Demônios", "demonios"),
-            Pair("Distopia", "distopia"),
-            Pair("Documentário", "documentario"),
-            Pair("Drama", "drama"),
-            Pair("Ecchi", "ecchi"),
-            Pair("Escola", "escola"),
-            Pair("Escolar", "escolar"),
-            Pair("Espaço", "espaco"),
-            Pair("Esporte", "esporte"),
-            Pair("Esportes", "esportes"),
-            Pair("Fantasia", "fantasia"),
-            Pair("Ficção Científica", "ficcao-cientifica"),
-            Pair("Futebol", "futebol"),
-            Pair("Game", "game"),
-            Pair("Girl battleships", "girl-battleships"),
-            Pair("Gourmet", "gourmet"),
-            Pair("Gundam", "gundam"),
-            Pair("Harém", "harem"),
-            Pair("Hentai", "hentai"),
-            Pair("Historia", "historia"),
-            Pair("Historial", "historial"),
-            Pair("Historical", "historical"),
-            Pair("Histórico", "historico"),
-            Pair("Horror", "horror"),
-            Pair("Humor Negro", "humor-negro"),
-            Pair("Ídolo", "idolo"),
-            Pair("Infantis", "infantis"),
-            Pair("Investigação", "investigacao"),
-            Pair("Isekai", "isekai"),
-            Pair("Jogo", "jogo"),
-            Pair("Jogos", "jogos"),
-            Pair("Josei", "josei"),
-            Pair("Kids", "kids"),
-            Pair("Luta", "luta"),
-            Pair("Maduro", "maduro"),
-            Pair("Máfia", "mafia"),
-            Pair("Magia", "magia"),
-            Pair("Mágica", "magica"),
-            Pair("Mecha", "mecha"),
-            Pair("Militar", "militar"),
-            Pair("Militares", "militares"),
-            Pair("Mistério", "misterio"),
-            Pair("Música", "musica"),
-            Pair("Musical", "musical"),
-            Pair("Não Informado!", "nao-informado"),
-            Pair("Paródia", "parodia"),
-            Pair("Piratas", "piratas"),
-            Pair("Polícia", "policia"),
-            Pair("Policial", "policial"),
-            Pair("Político", "politico"),
-            Pair("Pós-Apocalíptico", "pos-apocaliptico"),
-            Pair("Psico", "psico"),
-            Pair("Psicológico", "psicologico"),
-            Pair("Romance", "romance"),
-            Pair("Samurai", "samurai"),
-            Pair("Samurais", "samurais"),
-            Pair("Sátiro", "satiro"),
-            Pair("School Life", "school-life"),
-            Pair("SciFi", "scifi"),
-            Pair("Sci-Fi", "sci-fi"),
-            Pair("Seinen", "seinen"),
-            Pair("Shotacon", "shotacon"),
-            Pair("Shoujo", "shoujo"),
-            Pair("Shoujo Ai", "shoujo-ai"),
-            Pair("Shounem", "shounem"),
-            Pair("Shounen", "shounen"),
-            Pair("Shounen-ai", "shounen-ai"),
-            Pair("Slice of Life", "slice-of-life"),
-            Pair("Sobrenatural", "sobrenatural"),
-            Pair("Space", "space"),
-            Pair("Supernatural", "supernatural"),
-            Pair("Super Poder", "super-poder"),
-            Pair("Super-Poderes", "super-poderes"),
-            Pair("Suspense", "suspense"),
-            Pair("tear-studio", "tear-studio"),
-            Pair("Terror", "terror"),
-            Pair("Thriller", "thriller"),
-            Pair("Tragédia", "tragedia"),
-            Pair("Vampiro", "vampiro"),
-            Pair("Vampiros", "vampiros"),
-            Pair("Vida Escolar", "vida-escolar"),
-            Pair("Yaoi", "yaoi"),
-            Pair("Yuri", "yuri"),
-            Pair("Zombie", "zombie"),
-        )
-    }
-}
diff --git a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/AnimesTC.kt b/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/AnimesTC.kt
deleted file mode 100644
index 1f0330d7..00000000
--- a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/AnimesTC.kt
+++ /dev/null
@@ -1,294 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animestc
-
-import android.app.Application
-import androidx.preference.ListPreference
-import androidx.preference.PreferenceScreen
-import eu.kanade.tachiyomi.animeextension.pt.animestc.dto.AnimeDto
-import eu.kanade.tachiyomi.animeextension.pt.animestc.dto.EpisodeDto
-import eu.kanade.tachiyomi.animeextension.pt.animestc.dto.ResponseDto
-import eu.kanade.tachiyomi.animeextension.pt.animestc.dto.VideoDto
-import eu.kanade.tachiyomi.animeextension.pt.animestc.extractors.LinkBypasser
-import eu.kanade.tachiyomi.animeextension.pt.animestc.extractors.SendcmExtractor
-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.googledriveextractor.GoogleDriveExtractor
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.awaitSuccess
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-import eu.kanade.tachiyomi.util.parseAs
-import kotlinx.serialization.json.Json
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.Request
-import okhttp3.Response
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import uy.kohesive.injekt.injectLazy
-import java.text.SimpleDateFormat
-import java.util.Locale
-
-class AnimesTC : ConfigurableAnimeSource, AnimeHttpSource() {
-
-    override val name = "AnimesTC"
-
-    override val baseUrl = "https://api2.animestc.com"
-
-    override val lang = "pt-BR"
-
-    override val supportsLatest = true
-
-    override fun headersBuilder() = super.headersBuilder()
-        .add("Referer", "$HOST_URL/")
-
-    private val preferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    private val json: Json by injectLazy()
-
-    // ============================== Popular ===============================
-    override fun popularAnimeRequest(page: Int) = GET("$baseUrl/series?order=id&direction=asc&page=1&top=true", headers)
-
-    override fun popularAnimeParse(response: Response): AnimesPage {
-        val data = response.parseAs<List<AnimeDto>>()
-        val animes = data.map(::searchAnimeFromObject)
-        return AnimesPage(animes, false)
-    }
-
-    // =============================== Latest ===============================
-    override fun latestUpdatesRequest(page: Int) = GET(HOST_URL, headers)
-
-    override fun latestUpdatesParse(response: Response): AnimesPage {
-        val doc = response.asJsoup()
-        val animes = doc.select("div > article.episode").map {
-            SAnime.create().apply {
-                val ahref = it.selectFirst("h3 > a.episode-info-title-orange")!!
-                title = ahref.text()
-                val slug = ahref.attr("href").substringAfterLast("/")
-                setUrlWithoutDomain("/series?slug=$slug")
-                thumbnail_url = it.selectFirst("img.episode-image")?.attr("abs:data-src")
-            }
-        }
-            .filter { it.thumbnail_url?.contains("/_nuxt/img/") == false }
-            .distinctBy { it.url }
-
-        return AnimesPage(animes, false)
-    }
-
-    // =============================== Search ===============================
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
-        val params = ATCFilters.getSearchParameters(filters)
-        val url = "$baseUrl/series?order=title&direction=asc&page=$page".toHttpUrl()
-            .newBuilder()
-            .addQueryParameter("type", params.type)
-            .addQueryParameter("search", query)
-            .addQueryParameter("year", params.year)
-            .addQueryParameter("releaseStatus", params.status)
-            .addQueryParameter("tag", params.genre)
-            .build()
-
-        return GET(url, headers)
-    }
-
-    override fun searchAnimeParse(response: Response): AnimesPage {
-        val data = response.parseAs<ResponseDto<AnimeDto>>()
-        val animes = data.items.map(::searchAnimeFromObject)
-        val hasNextPage = data.lastPage > data.page
-        return AnimesPage(animes, hasNextPage)
-    }
-
-    override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
-        return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
-            val slug = query.removePrefix(PREFIX_SEARCH)
-            client.newCall(GET("$baseUrl/series?slug=$slug"))
-                .awaitSuccess()
-                .use(::searchAnimeBySlugParse)
-        } else {
-            return super.getSearchAnime(page, query, filters)
-        }
-    }
-
-    override fun getFilterList(): AnimeFilterList = ATCFilters.FILTER_LIST
-
-    private fun searchAnimeFromObject(anime: AnimeDto) = SAnime.create().apply {
-        thumbnail_url = anime.cover.url
-        title = anime.title
-        setUrlWithoutDomain("/series/${anime.id}")
-    }
-
-    private fun searchAnimeBySlugParse(response: Response): AnimesPage {
-        val details = animeDetailsParse(response).apply {
-            setUrlWithoutDomain(response.request.url.toString())
-            initialized = true
-        }
-
-        return AnimesPage(listOf(details), false)
-    }
-
-    // =========================== Anime Details ============================
-    override fun animeDetailsParse(response: Response) = SAnime.create().apply {
-        val anime = response.getAnimeDto()
-        setUrlWithoutDomain("/series/${anime.id}")
-        title = anime.title
-        status = anime.status
-        thumbnail_url = anime.cover.url
-        artist = anime.producer
-        genre = anime.genres
-        description = buildString {
-            append(anime.synopsis + "\n")
-
-            anime.classification?.also { append("\nClassificação: ", it, " anos") }
-            anime.year?.also { append("\nAno de lançamento: ", it) }
-        }
-    }
-
-    // ============================== Episodes ==============================
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val id = response.getAnimeDto().id
-        return getEpisodeList(id)
-    }
-
-    private fun episodeListRequest(animeId: Int, page: Int) =
-        GET("$baseUrl/episodes?order=id&direction=desc&page=$page&seriesId=$animeId&specialOrder=true")
-
-    private fun getEpisodeList(animeId: Int, page: Int = 1): List<SEpisode> {
-        val response = client.newCall(episodeListRequest(animeId, page)).execute()
-        val parsed = response.parseAs<ResponseDto<EpisodeDto>>()
-        val episodes = parsed.items.map(::episodeFromObject)
-
-        if (parsed.page < parsed.lastPage) {
-            return episodes + getEpisodeList(animeId, page + 1)
-        } else {
-            return episodes
-        }
-    }
-
-    private fun episodeFromObject(episode: EpisodeDto) = SEpisode.create().apply {
-        name = episode.title
-        setUrlWithoutDomain("/episodes?slug=${episode.slug}")
-        episode_number = episode.number.toFloat()
-        date_upload = episode.created_at.toDate()
-    }
-
-    // ============================ Video Links =============================
-    private val sendcmExtractor by lazy { SendcmExtractor(client) }
-    private val gdriveExtractor by lazy { GoogleDriveExtractor(client, headers) }
-    private val linkBypasser by lazy { LinkBypasser(client, json) }
-
-    private val supportedPlayers = listOf("send", "drive")
-
-    override fun videoListParse(response: Response): List<Video> {
-        val videoDto = response.parseAs<ResponseDto<VideoDto>>().items.first()
-        val links = videoDto.links
-
-        val allLinks = listOf(links.low, links.medium, links.high).flatten()
-            .filter { it.name in supportedPlayers }
-
-        val online = links.online?.run {
-            filterNot { "mega" in it }.map {
-                Video(it, "Player ATC", it, headers)
-            }
-        }.orEmpty()
-
-        val videoId = videoDto.id
-
-        return online + allLinks.parallelCatchingFlatMapBlocking { extractVideosFromLink(it, videoId) }
-    }
-
-    private fun extractVideosFromLink(video: VideoDto.VideoLink, videoId: Int): List<Video> {
-        val playerUrl = linkBypasser.bypass(video, videoId)
-            ?: return emptyList()
-
-        val quality = when (video.quality) {
-            "low" -> "SD"
-            "medium" -> "HD"
-            "high" -> "FULLHD"
-            else -> "SD"
-        }
-
-        return when (video.name) {
-            "send" -> sendcmExtractor.videosFromUrl(playerUrl, quality)
-            "drive" -> {
-                val id = GDRIVE_REGEX.find(playerUrl)?.groupValues?.get(0) ?: return emptyList()
-                gdriveExtractor.videosFromUrl(id, "GDrive - $quality")
-            }
-            else -> emptyList()
-        }
-    }
-
-    // ============================== Settings ==============================
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        ListPreference(screen.context).apply {
-            key = PREF_QUALITY_KEY
-            title = PREF_QUALITY_TITLE
-            entries = PREF_QUALITY_ENTRIES
-            entryValues = PREF_QUALITY_ENTRIES
-            setDefaultValue(PREF_QUALITY_DEFAULT)
-            summary = "%s"
-        }.also(screen::addPreference)
-
-        ListPreference(screen.context).apply {
-            key = PREF_PLAYER_KEY
-            title = PREF_PLAYER_TITLE
-            entries = PREF_PLAYER_VALUES
-            entryValues = PREF_PLAYER_VALUES
-            setDefaultValue(PREF_PLAYER_DEFAULT)
-            summary = "%s"
-        }.also(screen::addPreference)
-    }
-
-    // ============================= Utilities ==============================
-    private fun Response.getAnimeDto(): AnimeDto {
-        val jsonString = body.string()
-        return try {
-            jsonString.parseAs<AnimeDto>()
-        } catch (e: Exception) {
-            // URL intent handler moment
-            jsonString.parseAs<ResponseDto<AnimeDto>>().items.first()
-        }
-    }
-
-    private fun String.toDate(): Long {
-        return try {
-            DATE_FORMATTER.parse(this)?.time
-        } catch (_: Throwable) { null } ?: 0L
-    }
-
-    override fun List<Video>.sort(): List<Video> {
-        val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
-        val player = preferences.getString(PREF_PLAYER_KEY, PREF_PLAYER_DEFAULT)!!
-        return sortedWith(
-            compareBy(
-                { it.quality.contains(player) },
-                { it.quality.contains("- $quality") },
-            ),
-        ).reversed()
-    }
-
-    companion object {
-        private val DATE_FORMATTER by lazy {
-            SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH)
-        }
-
-        const val PREFIX_SEARCH = "slug:"
-
-        private const val HOST_URL = "https://www.animestc.net"
-
-        private const val PREF_QUALITY_KEY = "pref_quality"
-        private const val PREF_QUALITY_TITLE = "Qualidade preferida"
-        private const val PREF_QUALITY_DEFAULT = "HD"
-        private val PREF_QUALITY_ENTRIES = arrayOf("SD", "HD", "FULLHD")
-
-        private const val PREF_PLAYER_KEY = "pref_player"
-        private const val PREF_PLAYER_TITLE = "Player preferido"
-        private const val PREF_PLAYER_DEFAULT = "Sendcm"
-        private val PREF_PLAYER_VALUES = arrayOf("Sendcm", "GDrive", "Player ATC")
-
-        private val GDRIVE_REGEX = Regex("[\\w-]{28,}")
-    }
-}
diff --git a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/AnimesTCUrlActivity.kt b/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/AnimesTCUrlActivity.kt
deleted file mode 100644
index 9fc85460..00000000
--- a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/AnimesTCUrlActivity.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animestc
-
-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://wwww.animestc.net/animes/<item> intents
- * and redirects them to the main Aniyomi process.
- */
-class AnimesTCUrlActivity : 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", "${AnimesTC.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)
-    }
-}
diff --git a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/dto/AnimesTCDto.kt b/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/dto/AnimesTCDto.kt
deleted file mode 100644
index e8de368d..00000000
--- a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/dto/AnimesTCDto.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animestc.dto
-
-import eu.kanade.tachiyomi.animesource.model.SAnime
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-
-@Serializable
-data class ResponseDto<T>(
-    @SerialName("data")
-    val items: List<T>,
-    val lastPage: Int,
-    val page: Int,
-)
-
-@Serializable
-data class AnimeDto(
-    val classification: String?,
-    val cover: CoverDto,
-    val id: Int,
-    val producer: String?,
-    val releaseStatus: String,
-    val synopsis: String,
-    val tags: List<TagDto>,
-    val title: String,
-    val year: Int?,
-) {
-    val status by lazy {
-        when (releaseStatus) {
-            "complete" -> SAnime.COMPLETED
-            "airing" -> SAnime.ONGOING
-            else -> SAnime.UNKNOWN
-        }
-    }
-
-    val genres by lazy { tags.joinToString(", ") { it.name } }
-
-    @Serializable
-    data class TagDto(val name: String)
-}
-
-@Serializable
-data class EpisodeDto(
-    @SerialName("seriesId")
-    val animeId: Int,
-    val cover: CoverDto?,
-    val created_at: String,
-    val number: String,
-    val slug: String,
-    val title: String,
-)
-
-@Serializable
-data class VideoDto(
-    val id: Int,
-    val links: VideoLinksDto,
-) {
-    @Serializable
-    data class VideoLinksDto(
-        val low: List<VideoLink> = emptyList(),
-        val medium: List<VideoLink> = emptyList(),
-        val high: List<VideoLink> = emptyList(),
-        val online: List<String>? = null,
-    )
-
-    @Serializable
-    data class VideoLink(
-        val index: Int,
-        val name: String,
-        val quality: String,
-    )
-}
-
-@Serializable
-data class CoverDto(
-    val originalName: String,
-) {
-    val url by lazy { "https://stc.animestc.com/$originalName" }
-}
diff --git a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/extractors/LinkBypasser.kt b/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/extractors/LinkBypasser.kt
deleted file mode 100644
index afd58a59..00000000
--- a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/extractors/LinkBypasser.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animestc.extractors
-
-import android.util.Base64
-import eu.kanade.tachiyomi.animeextension.pt.animestc.dto.VideoDto.VideoLink
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.util.asJsoup
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-import okhttp3.OkHttpClient
-
-class LinkBypasser(
-    private val client: OkHttpClient,
-    private val json: Json,
-) {
-    fun bypass(video: VideoLink, episodeId: Int): String? {
-        val joined = "$episodeId/${video.quality}/${video.index}"
-        val encoded = Base64.encodeToString(joined.toByteArray(), Base64.NO_WRAP)
-        val url = "$PROTECTOR_URL/link/$encoded"
-        val res = client.newCall(GET(url)).execute()
-        if (res.code != 200) {
-            return null
-        }
-
-        // Sadly we MUST wait 6s or we are going to get a HTTP 500
-        Thread.sleep(6000L)
-        val id = res.asJsoup().selectFirst("meta#link-id")!!.attr("value")
-        val apiCall = client.newCall(GET("$PROTECTOR_URL/api/link/$id")).execute()
-        if (apiCall.code != 200) {
-            return null
-        }
-
-        val apiBody = apiCall.body.string()
-        return json.decodeFromString<LinkDto>(apiBody).link
-    }
-
-    @Serializable
-    data class LinkDto(val link: String)
-
-    companion object {
-        private const val PROTECTOR_URL = "https://protetor.animestc.xyz"
-    }
-}
diff --git a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/extractors/SendcmExtractor.kt b/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/extractors/SendcmExtractor.kt
deleted file mode 100644
index c422c543..00000000
--- a/src/pt/animestc/src/eu/kanade/tachiyomi/animeextension/pt/animestc/extractors/SendcmExtractor.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animestc.extractors
-
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.util.asJsoup
-import okhttp3.Headers
-import okhttp3.OkHttpClient
-
-class SendcmExtractor(private val client: OkHttpClient) {
-    private val playerName = "Sendcm"
-
-    fun videosFromUrl(url: String, quality: String): List<Video> {
-        val doc = client.newCall(GET(url)).execute().asJsoup()
-        val videoUrl = doc.selectFirst("video#vjsplayer > source")?.attr("src")
-        return videoUrl?.let {
-            val headers = Headers.headersOf("Referer", url)
-            listOf(Video(it, "$playerName - $quality", it, headers = headers))
-        }.orEmpty()
-    }
-}
diff --git a/src/pt/animeszone/AndroidManifest.xml b/src/pt/animeszone/AndroidManifest.xml
deleted file mode 100644
index bc031e78..00000000
--- a/src/pt/animeszone/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <application>
-        <activity
-            android:name=".pt.animeszone.AZUrlActivity"
-            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="animeszone.net"
-                    android:pathPattern="/..*/..*"
-                    android:scheme="https" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/src/pt/animeszone/build.gradle b/src/pt/animeszone/build.gradle
deleted file mode 100644
index 2bcfcb39..00000000
--- a/src/pt/animeszone/build.gradle
+++ /dev/null
@@ -1,12 +0,0 @@
-ext {
-    extName = 'AnimesZone'
-    extClass = '.AnimesZone'
-    extVersionCode = 12
-    isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(':lib:dood-extractor'))
-}
diff --git a/src/pt/animeszone/res/mipmap-hdpi/ic_launcher.png b/src/pt/animeszone/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 2d1732e1..00000000
Binary files a/src/pt/animeszone/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animeszone/res/mipmap-mdpi/ic_launcher.png b/src/pt/animeszone/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 0fb7c62e..00000000
Binary files a/src/pt/animeszone/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animeszone/res/mipmap-xhdpi/ic_launcher.png b/src/pt/animeszone/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index be1ee581..00000000
Binary files a/src/pt/animeszone/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animeszone/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/animeszone/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 489695ae..00000000
Binary files a/src/pt/animeszone/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animeszone/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/animeszone/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 39fb6769..00000000
Binary files a/src/pt/animeszone/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/AZUrlActivity.kt b/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/AZUrlActivity.kt
deleted file mode 100644
index e701aadf..00000000
--- a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/AZUrlActivity.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animeszone
-
-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://animeszone.net/<type>/<slug> intents
- * and redirects them to the main Aniyomi process.
- */
-class AZUrlActivity : 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 searchQuery = "${pathSegments[0]}/${pathSegments[1]}"
-
-            val mainIntent = Intent().apply {
-                action = "eu.kanade.tachiyomi.ANIMESEARCH"
-                putExtra("query", "${AnimesZone.PREFIX_SEARCH}$searchQuery")
-                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)
-    }
-}
diff --git a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/AnimesZone.kt b/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/AnimesZone.kt
deleted file mode 100644
index 04f40496..00000000
--- a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/AnimesZone.kt
+++ /dev/null
@@ -1,439 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animeszone
-
-import android.app.Application
-import androidx.preference.ListPreference
-import androidx.preference.PreferenceScreen
-import app.cash.quickjs.QuickJs
-import eu.kanade.tachiyomi.animeextension.pt.animeszone.extractors.BloggerJWPlayerExtractor
-import eu.kanade.tachiyomi.animeextension.pt.animeszone.extractors.PlaylistExtractor
-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.ParsedAnimeHttpSource
-import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
-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 eu.kanade.tachiyomi.util.parseAs
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-import okhttp3.MediaType.Companion.toMediaType
-import okhttp3.Request
-import okhttp3.RequestBody.Companion.toRequestBody
-import okhttp3.Response
-import org.jsoup.Jsoup
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import uy.kohesive.injekt.injectLazy
-
-class AnimesZone : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
-
-    override val name = "AnimesZone"
-
-    override val baseUrl = "https://animeszone.net"
-
-    override val lang = "pt-BR"
-
-    override val supportsLatest = true
-
-    override fun headersBuilder() = super.headersBuilder()
-        .add("Referer", "$baseUrl/")
-        .add("Origin", baseUrl)
-
-    private val json: Json by injectLazy()
-
-    private val preferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    // ============================== Popular ===============================
-    override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/tendencia/")
-
-    override fun popularAnimeSelector(): String = "div.items > div.seriesList"
-
-    override fun popularAnimeNextPageSelector(): String? = null
-
-    override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
-        setUrlWithoutDomain(element.selectFirst("a[href]")!!.attr("abs:href"))
-        thumbnail_url = element.selectFirst("div.cover-image")?.run {
-            attr("style").substringAfter("url('").substringBefore("'")
-        }
-        title = element.selectFirst("span.series-title")!!.text()
-    }
-
-    // =============================== Latest ===============================
-    override fun latestUpdatesRequest(page: Int): Request {
-        return if (page == 1) {
-            GET("$baseUrl/animes-legendados/")
-        } else {
-            GET("$baseUrl/animes-legendados/page/$page/")
-        }
-    }
-
-    override fun latestUpdatesSelector(): String = "main#list-animes ul.post-lst > li"
-
-    override fun latestUpdatesNextPageSelector(): String = "div.paginadorplay > a.active + a"
-
-    override fun latestUpdatesFromElement(element: Element) = SAnime.create().apply {
-        setUrlWithoutDomain(element.selectFirst("div.aniItem > a[href]")!!.attr("abs:href"))
-        thumbnail_url = element.selectFirst("div.aniItemImg img[src]")?.attr("abs:src")
-        title = element.selectFirst("h2.aniTitulo")!!.text()
-    }
-
-    // =============================== Search ===============================
-    override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
-        return if (query.startsWith(PREFIX_SEARCH)) {
-            val path = query.removePrefix(PREFIX_SEARCH)
-            client.newCall(GET("$baseUrl/$path"))
-                .awaitSuccess()
-                .use(::searchAnimeByIdParse)
-        } else {
-            super.getSearchAnime(page, query, filters)
-        }
-    }
-
-    private fun searchAnimeByIdParse(response: Response): AnimesPage {
-        val details = animeDetailsParse(response).apply {
-            setUrlWithoutDomain(response.request.url.toString())
-            initialized = true
-        }
-
-        return AnimesPage(listOf(details), false)
-    }
-
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
-        val params = AnimesZoneFilters.getSearchParameters(filters)
-
-        val cleanQuery = query.replace(" ", "%20")
-        val queryQuery = if (query.isBlank()) {
-            ""
-        } else {
-            "&_pesquisa=$cleanQuery"
-        }
-        val url = "$baseUrl/?s=${params.genre}${params.year}${params.version}${params.studio}${params.type}${params.adult}$queryQuery"
-
-        val httpParams = url.substringAfter("$baseUrl/?").split("&").joinToString(",") {
-            val (key, value) = it.split("=", limit = 2)
-            "\"$key\":\"$value\""
-        }
-        val softRefresh = if (page == 1) 0 else 1
-        val jsonBody = """
-            {
-               "action":"facetwp_refresh",
-               "data":{
-                  "facets":{
-                     "generos":[
-                        ${queryToJsonList(params.genre)}
-                     ],
-                     "versao":[
-                        ${queryToJsonList(params.year)}
-                     ],
-                     "tipo":[
-                        ${queryToJsonList(params.version)}
-                     ],
-                     "estudio":[
-                        ${queryToJsonList(params.studio)}
-                     ],
-                     "tipototal":[
-                        ${queryToJsonList(params.type)}
-                     ],
-                     "adulto":[
-                        ${queryToJsonList(params.adult)}
-                     ],
-                     "pesquisa":"$query",
-                     "paginar":[
-
-                     ]
-                  },
-                  "frozen_facets":{
-
-                  },
-                  "http_params":{
-                     "get":{
-                        $httpParams
-                     },
-                     "uri":"",
-                     "url_vars":[
-
-                     ]
-                  },
-                  "template":"wp",
-                  "extras":{
-                     "sort":"default"
-                  },
-                  "soft_refresh":$softRefresh,
-                  "is_bfcache":1,
-                  "first_load":0,
-                  "paged":$page
-               }
-            }
-        """.trimIndent().toRequestBody("application/json".toMediaType())
-
-        return POST(url, headers, jsonBody)
-    }
-
-    override fun searchAnimeParse(response: Response): AnimesPage {
-        val parsed = response.parseAs<PostResponse>()
-
-        val document = Jsoup.parse(parsed.template)
-
-        val animes = document.select(searchAnimeSelector()).map(::searchAnimeFromElement)
-
-        val hasNextPage = parsed.settings.pager.run { page < total_pages }
-
-        return AnimesPage(animes, hasNextPage)
-    }
-
-    override fun searchAnimeSelector(): String = "div.aniItem"
-
-    override fun searchAnimeNextPageSelector(): String? = null
-
-    override fun searchAnimeFromElement(element: Element) = SAnime.create().apply {
-        setUrlWithoutDomain(element.selectFirst("a[href]")!!.attr("href"))
-        thumbnail_url = element.selectFirst("img[src]")?.attr("abs:src")
-        title = element.selectFirst("div.aniInfos")?.text() ?: "Anime"
-    }
-
-    // ============================== FILTERS ===============================
-    override fun getFilterList(): AnimeFilterList = AnimesZoneFilters.FILTER_LIST
-
-    // =========================== Anime Details ============================
-    override fun animeDetailsParse(document: Document) = SAnime.create().apply {
-        title = document.selectFirst("div.container > div div.bottom-div > h4")?.ownText()
-            ?: document.selectFirst("div#info > h1")?.text()
-            ?: "Anime"
-        thumbnail_url = document.selectFirst("div.container > div > img[src]")?.attr("abs:src")
-        description = document.selectFirst("section#sinopse p:matches(.)")?.text()
-            ?: document.selectFirst("div.content.right > dialog > p:matches(.)")?.text()
-        genre = document.select("div.card-body table > tbody > tr:has(>td:contains(Genres)) td > a").joinToString { it.text() }
-    }
-
-    // ============================== Episodes ==============================
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val document = response.asJsoup()
-
-        val singleVideo = document.selectFirst("div.anime__video__player")
-
-        // First check if single episode
-        return if (singleVideo != null) {
-            SEpisode.create().apply {
-                name = document.selectFirst("div#info h1")?.text() ?: "Episódio"
-                episode_number = 1F
-                setUrlWithoutDomain(document.location())
-            }.let(::listOf)
-        } else {
-            buildList {
-                document.select(episodeListSelector()).forEach { ep ->
-                    val name = ep.selectFirst("h2.aniTitulo")?.text()?.trim()
-                    // Check if it's multi-season
-                    var nextPageUrl = when {
-                        name != null && name.startsWith("temporada ", true) -> ep.selectFirst("a[href]")!!.attr("href")
-                        else -> {
-                            add(episodeFromElement(ep, size + 1))
-                            document.nextPageUrl()
-                        }
-                    }
-
-                    while (nextPageUrl != null) {
-                        val seasonDocument = client.newCall(GET(nextPageUrl)).execute()
-                            .asJsoup()
-
-                        seasonDocument.select(episodeListSelector()).forEach { seasonEp ->
-                            add(episodeFromElement(seasonEp, size + 1, name))
-                        }
-
-                        nextPageUrl = seasonDocument.nextPageUrl()
-                    }
-                }
-
-                reverse()
-            }
-        }
-    }
-
-    private fun Document.nextPageUrl() = selectFirst("div.paginadorplay > a:contains(Próxima Pagina)")?.absUrl("href")
-
-    override fun episodeListSelector(): String = "main.site-main ul.post-lst > li"
-
-    private fun episodeFromElement(element: Element, counter: Int, info: String? = null): SEpisode {
-        val epTitle = element.selectFirst("div.title")?.text() ?: element.text()
-        val epNumber = element.selectFirst("span.epiTipo")
-
-        return SEpisode.create().apply {
-            name = "Ep. ${epNumber?.text()?.trim() ?: counter} - ${epTitle.replace(EPISODE_REGEX, "")}"
-                .replace(" - - ", " - ")
-            episode_number = epNumber?.run {
-                text().trim().toFloatOrNull()
-            } ?: counter.toFloat()
-            scanlator = info
-            setUrlWithoutDomain(element.selectFirst("article > a")!!.attr("href"))
-        }
-    }
-
-    override fun episodeFromElement(element: Element): SEpisode = throw UnsupportedOperationException()
-
-    // ============================ Video Links =============================
-    override fun videoListParse(response: Response): List<Video> {
-        val document = response.asJsoup()
-
-        val videoList = document.select("div#playeroptions li[data-post]").flatMap { vid ->
-            val jsonHeaders = headersBuilder()
-                .add("Accept", "application/json, text/javascript, */*; q=0.01")
-                .add("X-Requested-With", "XMLHttpRequest")
-                .build()
-
-            val post = vid.attr("data-post")
-            val type = vid.attr("data-type")
-            val nume = vid.attr("data-nume")
-
-            val apires = client.newCall(
-                GET("$baseUrl/wp-json/dooplayer/v2/$post/$type/$nume", jsonHeaders),
-            ).execute()
-
-            val url = apires.parseAs<VideoResponse>().embed_url
-
-            when {
-                url.startsWith("https://dood") -> DoodExtractor(client).videosFromUrl(url, vid.text().trim())
-                "https://gojopoolt" in url -> {
-                    client.newCall(GET(url, headers)).execute()
-                        .asJsoup()
-                        .selectFirst("script:containsData(sources:)")
-                        ?.data()
-                        ?.let(BloggerJWPlayerExtractor::videosFromScript)
-                        .orEmpty()
-                }
-                url.startsWith(baseUrl) -> videosFromInternalUrl(url)
-                "blogger.com" in url -> extractBloggerVideos(url, vid.text().trim())
-                else -> emptyList()
-            }
-        }
-
-        return videoList
-    }
-
-    private fun videosFromInternalUrl(url: String): List<Video> {
-        val videoDocument = client.newCall(GET(url, headers)).execute()
-            .asJsoup()
-
-        val script = videoDocument.selectFirst("script:containsData(decodeURIComponent)")?.data()
-            ?.let(::getDecrypted)
-            ?: videoDocument.selectFirst("script:containsData(sources:)")?.data()
-            ?: return emptyList()
-
-        return when {
-            "/bloggerjwplayer" in url || "jwplayer-2" in url || "/ctaplayer" in url -> {
-                BloggerJWPlayerExtractor.videosFromScript(script)
-            }
-            "/m3u8" in url -> PlaylistExtractor.videosFromScript(script)
-            else -> emptyList()
-        }
-    }
-
-    private fun extractBloggerVideos(url: String, name: String): List<Video> {
-        return client.newCall(GET(url, headers)).execute()
-            .body.string()
-            .takeIf { !it.contains("errorContainer") }
-            .let { it ?: return emptyList() }
-            .substringAfter("\"streams\":[")
-            .substringBefore("]")
-            .split("},")
-            .map {
-                val videoUrl = it.substringAfter("{\"play_url\":\"").substringBefore('"')
-                val format = it.substringAfter("\"format_id\":").substringBefore("}")
-                val quality = when (format) {
-                    "18" -> "360p"
-                    "22" -> "720p"
-                    else -> "Unknown"
-                }
-                Video(videoUrl, "$quality - $name", videoUrl, headers = headers)
-            }
-    }
-
-    override fun videoFromElement(element: Element): Video = throw UnsupportedOperationException()
-
-    override fun videoListSelector(): String = throw UnsupportedOperationException()
-
-    override fun videoUrlParse(document: Document): String = throw UnsupportedOperationException()
-
-    // ============================== Settings ==============================
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        ListPreference(screen.context).apply {
-            key = PREF_QUALITY_KEY
-            title = PREF_QUALITY_TITLE
-            entries = PREF_QUALITY_ENTRIES
-            entryValues = PREF_QUALITY_VALUES
-            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)
-    }
-
-    // ============================= Utilities ==============================
-    private fun getDecrypted(script: String): String? {
-        val patchedScript = script.trim().split("\n").first()
-            .replace("eval(function", "function a")
-            .replace("decodeURIComponent(escape(r))}(", "r};a(")
-            .substringBeforeLast(")")
-
-        return QuickJs.create().use {
-            it.evaluate(patchedScript)?.toString()
-        }
-    }
-
-    private fun queryToJsonList(input: String): String {
-        if (input.isEmpty()) return ""
-        return input.substringAfter("=").split("%2C").joinToString(",") {
-            "\"$it\""
-        }
-    }
-
-    @Serializable
-    data class VideoResponse(val embed_url: String)
-
-    @Serializable
-    data class PostResponse(
-        val template: String,
-        val settings: Settings,
-    ) {
-        @Serializable
-        data class Settings(val pager: Pager) {
-            @Serializable
-            data class Pager(
-                val page: Int,
-                val total_pages: Int,
-            )
-        }
-    }
-
-    override fun List<Video>.sort(): List<Video> {
-        val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
-
-        return sortedWith(
-            compareBy { it.quality.contains(quality) },
-        ).reversed()
-    }
-
-    companion object {
-        const val PREFIX_SEARCH = "id:"
-
-        private val EPISODE_REGEX by lazy { Regex("""Episódio ?\d+\.?\d* ?""") }
-
-        private const val PREF_QUALITY_KEY = "preferred_quality"
-        private const val PREF_QUALITY_TITLE = "Qualidade preferida"
-        private const val PREF_QUALITY_DEFAULT = "720"
-        private val PREF_QUALITY_ENTRIES = arrayOf("1080p", "720p", "480p", "360p")
-        private val PREF_QUALITY_VALUES = arrayOf("1080", "720", "480", "360")
-    }
-}
diff --git a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/AnimesZoneFilters.kt b/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/AnimesZoneFilters.kt
deleted file mode 100644
index 73e82ee7..00000000
--- a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/AnimesZoneFilters.kt
+++ /dev/null
@@ -1,279 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animeszone
-
-import eu.kanade.tachiyomi.animesource.model.AnimeFilter
-import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
-
-object AnimesZoneFilters {
-    open class QueryPartFilter(
-        displayName: String,
-        val vals: Array<Pair<String, String>>,
-    ) : AnimeFilter.Select<String>(
-        displayName,
-        vals.map { it.first }.toTypedArray(),
-    ) {
-        fun toQueryPart() = vals[state].second
-    }
-
-    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.asQueryPart(name: String): String {
-        return (first { it is R } as QueryPartFilter).toQueryPart()
-            .takeUnless(String::isEmpty)
-            ?.let { "&$name=$it" }
-            .orEmpty()
-    }
-
-    private inline fun <reified R> AnimeFilterList.parseCheckbox(
-        options: Array<Pair<String, String>>,
-        name: String,
-    ): String {
-        return (first { it is R } as CheckBoxFilterList).state
-            .asSequence()
-            .filter { it.state }
-            .map { checkbox -> options.find { it.first == checkbox.name }!!.second }
-            .filter(String::isNotBlank)
-            .joinToString("%2C") { "&$name=$it" }
-    }
-
-    class GenreFilter : CheckBoxFilterList(
-        "Selecionar Gêneros",
-        AnimesZoneFiltersData.GENRE.map { CheckBoxVal(it.first, false) },
-    )
-
-    class YearFilter : QueryPartFilter("Ano", AnimesZoneFiltersData.YEAR)
-
-    class VersionFilter : QueryPartFilter("Versão", AnimesZoneFiltersData.VERSION)
-
-    class StudioFilter : CheckBoxFilterList(
-        "Estudio",
-        AnimesZoneFiltersData.STUDIO.map { CheckBoxVal(it.first, false) },
-    )
-
-    class TypeFilter : QueryPartFilter("Tipo", AnimesZoneFiltersData.TYPE)
-
-    class AdultFilter : QueryPartFilter("Adulto", AnimesZoneFiltersData.ADULT)
-
-    val FILTER_LIST get() = AnimeFilterList(
-        GenreFilter(),
-        StudioFilter(),
-        YearFilter(),
-        VersionFilter(),
-        TypeFilter(),
-        AdultFilter(),
-    )
-
-    data class FilterSearchParams(
-        val genre: String = "",
-        val year: String = "",
-        val version: String = "",
-        val studio: String = "",
-        val type: String = "",
-        val adult: String = "",
-    )
-
-    internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
-        if (filters.isEmpty()) return FilterSearchParams()
-
-        return FilterSearchParams(
-            filters.parseCheckbox<GenreFilter>(AnimesZoneFiltersData.GENRE, "_generos"),
-            filters.asQueryPart<YearFilter>("_versao"),
-            filters.asQueryPart<VersionFilter>("_tipo"),
-            filters.parseCheckbox<StudioFilter>(AnimesZoneFiltersData.STUDIO, "_estudio"),
-            filters.asQueryPart<TypeFilter>("_tipototal"),
-            filters.asQueryPart<AdultFilter>("_adulto"),
-        )
-    }
-
-    private object AnimesZoneFiltersData {
-        val ANY = Pair("Selecione", "")
-
-        val GENRE = arrayOf(
-            Pair("Comédia", "comedia"),
-            Pair("Ação", "acao"),
-            Pair("Fantasia", "fantasia"),
-            Pair("Romance", "romance"),
-            Pair("Drama", "drama"),
-            Pair("Escolar", "escolar"),
-            Pair("Aventura", "aventura"),
-            Pair("Shounen", "shounen"),
-            Pair("Sobrenatural", "sobrenatural"),
-            Pair("Slice-of-life", "slice-of-life"),
-            Pair("Sci-Fi", "sci-fi"),
-            Pair("Ecchi", "ecchi"),
-            Pair("Mistério", "misterio"),
-            Pair("Seinen", "seinen"),
-            Pair("Magia", "magia"),
-            Pair("Animação", "animacao"),
-            Pair("Harem", "harem"),
-            Pair("Psicológico", "psicologico"),
-            Pair("Super Poder", "super-poder"),
-            Pair("Violência", "violencia"),
-            Pair("Sci-Fi & Fantasy", "sci-fi-fantasy"),
-            Pair("Histórico", "historico"),
-            Pair("Isekai", "isekai"),
-            Pair("Mecha", "mecha"),
-            Pair("Demónio", "demonio"),
-            Pair("Terror", "terror"),
-            Pair("Esportes", "esportes"),
-            Pair("Militar", "militar"),
-            Pair("Artes Marciais", "artes-marciais"),
-            Pair("Jogo", "jogo"),
-            Pair("Vampiro", "vampiro"),
-            Pair("Musical", "musical"),
-            Pair("Suspense", "suspense"),
-            Pair("Paródia", "parodia"),
-            Pair("Shoujo", "shoujo"),
-            Pair("Nudez", "nudez"),
-            Pair("Supernatural", "supernatural"),
-            Pair("Espacial", "espacial"),
-            Pair("Shoujo-ai", "shoujo-ai"),
-            Pair("Crime", "crime"),
-            Pair("Policial", "policial"),
-            Pair("Ficção Científica", "ficcao-cientifica"),
-            Pair("Samurai", "samurai"),
-            Pair("Josei", "josei"),
-            Pair("Action & Adventure", "action-adventure"),
-            Pair("Amizade", "amizade"),
-            Pair("Horror", "horror"),
-            Pair("Família", "familia"),
-            Pair("Música", "musica"),
-            Pair("Insanidade", "insanidade"),
-            Pair("Obsceno", "obsceno"),
-            Pair("Shounen-ai", "shounen-ai"),
-            Pair("Carros", "carros"),
-            Pair("Gore", "gore"),
-            Pair("War & Politics", "war-politics"),
-            Pair("Yaoi", "yaoi"),
-            Pair("Cinema TV", "cinema-tv"),
-            Pair("Gourmet", "gourmet"),
-            Pair("Infantil", "infantil"),
-            Pair("Vida Escolar", "vida-escolar"),
-        )
-
-        val YEAR = arrayOf(ANY) + (1986..2024).map {
-            Pair(it.toString(), it.toString())
-        }.reversed().toTypedArray()
-
-        val VERSION = arrayOf(
-            ANY,
-            Pair("Legendados", "legendada"),
-            Pair("Dublado", "series02"),
-            Pair("Seção de Hentais", "series03"),
-        )
-
-        val STUDIO = arrayOf(
-            Pair("J.C.Staff", "j-c-staff"),
-            Pair("Shueisha", "shueisha"),
-            Pair("Aniplex", "aniplex"),
-            Pair("BONES", "bones"),
-            Pair("Kadokawa", "kadokawa"),
-            Pair("TOHO Animation", "toho-animation"),
-            Pair("Pony Canyon", "pony-canyon"),
-            Pair("A-1 Pictures", "a-1-pictures"),
-            Pair("DENTSU", "dentsu"),
-            Pair("Kodansha", "kodansha"),
-            Pair("Production I.G", "production-i-g"),
-            Pair("CloverWorks", "cloverworks"),
-            Pair("Madhouse", "madhouse"),
-            Pair("Bit grooove promotion", "bit-grooove-promotion"),
-            Pair("MAPPA", "mappa"),
-            Pair("SILVER LINK.", "silver-link"),
-            Pair("Wit Studio", "wit-studio"),
-            Pair("Magic Capsule", "magic-capsule"),
-            Pair("OLM", "olm"),
-            Pair("Lantis", "lantis"),
-            Pair("Movic", "movic"),
-            Pair("SATELIGHT", "satelight"),
-            Pair("Shogakukan-Shueisha Productions", "shogakukan-shueisha-productions"),
-            Pair("Square Enix", "square-enix"),
-            Pair("STUDIO MAUSU", "studio-mausu"),
-            Pair("Yomiuri Telecasting Corporation", "yomiuri-telecasting-corporation"),
-            Pair("Bandai Namco Arts", "bandai-namco-arts"),
-            Pair("David Production", "david-production"),
-            Pair("EGG FIRM", "egg-firm"),
-            Pair("Lerche", "lerche"),
-            Pair("Liden Films", "liden-films"),
-            Pair("Sony Music Entertainment", "sony-music-entertainment-japan"),
-            Pair("Studio Deen", "studio-deen"),
-            Pair("TMS Entertainment", "tms-entertainment"),
-            Pair("Toho", "toho"),
-            Pair("Crunchyroll", "crunchyroll"),
-            Pair("dugout", "dugout"),
-            Pair("ENGI", "engi"),
-            Pair("MBS", "mbs"),
-            Pair("P.A.Works", "p-a-works"),
-            Pair("Tezuka Productions", "tezuka-productions"),
-            Pair("TV Tokyo", "tv-tokyo"),
-            Pair("Warner Bros. Japan", "warner-bros-japan"),
-            Pair("White Fox", "white-fox"),
-            Pair("avex pictures", "avex-pictures"),
-            Pair("Bibury Animation Studios", "bibury-animation-studios"),
-            Pair("Brain's Base", "brains-base"),
-            Pair("DMM music", "dmm-music"),
-            Pair("DMM pictures", "dmm-pictures"),
-            Pair("feel.", "feel"),
-            Pair("Hakuhodo DY Music & Pictures", "hakuhodo-dy-music-pictures"),
-            Pair("Lidenfilms", "lidenfilms"),
-            Pair("MAHO FILM", "maho-film"),
-            Pair("NHK Enterprises", "nhk-enterprises"),
-            Pair("Passione", "passione"),
-            Pair("Pierrot", "pierrot"),
-            Pair("Pine Jam", "pine-jam"),
-            Pair("Pink Pineapple", "pink-pineapple"),
-            Pair("project No.9", "project-no-9"),
-            Pair("Seven", "seven"),
-            Pair("SHAFT", "shaft"),
-            Pair("TNK", "tnk"),
-            Pair("Zero-G", "zero-g"),
-            Pair("Asahi Production", "asahi-production"),
-            Pair("asread", "asread"),
-            Pair("AT-X", "at-x"),
-            Pair("Bandai Namco Pictures", "bandai-namco-pictures"),
-            Pair("BS Fuji", "bs-fuji"),
-            Pair("C2C", "c2c"),
-            Pair("Children's Playground Entertainment", "childrens-playground-entertainment"),
-            Pair("diomedéa", "diomedea"),
-            Pair("Doga Kobo", "doga-kobo"),
-            Pair("Geno Studio", "geno-studio"),
-            Pair("Good Smile Company", "good-smile-company"),
-            Pair("Graphinica", "graphinica"),
-            Pair("Hakusensha", "hakusensha"),
-            Pair("HALF H·P STUDIO", "f279ee47217fbae84c07eb11181f4997"),
-            Pair("King Records", "king-records"),
-            Pair("Kyoto Animation", "kyoto-animation"),
-            Pair("Nippon BS Broadcasting", "nippon-bs-broadcasting"),
-            Pair("Nippon Columbia", "nippon-columbia"),
-            Pair("Nitroplus", "nitroplus"),
-            Pair("Shogakukan", "shogakukan"),
-            Pair("Sotsu", "sotsu"),
-            Pair("Sound Team・Don Juan", "45e6f4604baaebfbebf4f43139db8d68"),
-            Pair("Studio Gokumi", "studio-gokumi"),
-            Pair("Suiseisha", "suiseisha"),
-            Pair("SUNRISE", "sunrise"),
-            Pair("SynergySP", "synergysp"),
-            Pair("Techno Sound", "techno-sound"),
-            Pair("THE KLOCKWORX", "the-klockworx"),
-            Pair("Toei Animation", "toei-animation"),
-            Pair("TOY'S FACTORY", "toys-factory"),
-            Pair("Twin Engine", "twin-engine"),
-            Pair("ufotable", "ufotable"),
-            Pair("ABC Animation", "abc-animation"),
-            Pair("Ajiado", "ajiado"),
-            Pair("APDREAM", "apdream"),
-            Pair("Ashi Productions", "ashi-productions"),
-        )
-
-        val TYPE = arrayOf(
-            ANY,
-            Pair("TV Shows", "tvshows"),
-            Pair("Filmes", "movies"),
-        )
-
-        val ADULT = arrayOf(
-            ANY,
-            Pair("Hentais", "dublada"),
-            Pair("Seção de Hentais", "series03"),
-        )
-    }
-}
diff --git a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/extractors/BloggerJWPlayerExtractor.kt b/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/extractors/BloggerJWPlayerExtractor.kt
deleted file mode 100644
index c655a3e8..00000000
--- a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/extractors/BloggerJWPlayerExtractor.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animeszone.extractors
-
-import eu.kanade.tachiyomi.animesource.model.Video
-
-object BloggerJWPlayerExtractor {
-    fun videosFromScript(script: String): List<Video> {
-        val sources = script.substringAfter("sources: [").substringBefore("],")
-
-        return sources.split("{").drop(1).mapNotNull {
-            val label = it.substringAfter("label")
-                .substringAfter(':')
-                .substringAfter('"')
-                .substringBefore('"')
-
-            val videoUrl = it.substringAfter("file")
-                .substringAfter(':')
-                .substringAfter('"')
-                .substringBefore('"')
-                .replace("\\", "")
-            if (videoUrl.isEmpty()) {
-                null
-            } else {
-                Video(videoUrl, "BloggerJWPlayer - $label", videoUrl)
-            }
-        }
-    }
-}
diff --git a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/extractors/PlaylistExtractor.kt b/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/extractors/PlaylistExtractor.kt
deleted file mode 100644
index 69d0d58c..00000000
--- a/src/pt/animeszone/src/eu/kanade/tachiyomi/animeextension/pt/animeszone/extractors/PlaylistExtractor.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.animeszone.extractors
-
-import eu.kanade.tachiyomi.animesource.model.Video
-
-object PlaylistExtractor {
-    fun videosFromScript(script: String): List<Video> {
-        val sources = script.substringAfter("sources: [").substringBefore("],")
-
-        return sources.split("file:\"").drop(1).mapNotNull { source ->
-            val url = source.substringBefore("\"").ifEmpty { return@mapNotNull null }
-            val label = source.substringAfter("label:\"").substringBefore("\"")
-                .replace("FHD", "1080p")
-                .replace("HD", "720p")
-                .replace("SD", "480p")
-            Video(url, "Playlist - $label", url)
-        }
-    }
-}
diff --git a/src/pt/anitube/build.gradle b/src/pt/anitube/build.gradle
index 468f70ee..9f22f339 100644
--- a/src/pt/anitube/build.gradle
+++ b/src/pt/anitube/build.gradle
@@ -1,7 +1,7 @@
 ext {
     extName = 'Anitube'
     extClass = '.Anitube'
-    extVersionCode = 22
+    extVersionCode = 24
 }
 
 apply from: "$rootDir/common.gradle"
diff --git a/src/pt/anitube/res/web_hi_res_512.png b/src/pt/anitube/res/web_hi_res_512.png
deleted file mode 100644
index a1f94f75..00000000
Binary files a/src/pt/anitube/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/extractors/AnitubeExtractor.kt b/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/extractors/AnitubeExtractor.kt
index 64856ada..1736832d 100644
--- a/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/extractors/AnitubeExtractor.kt
+++ b/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/extractors/AnitubeExtractor.kt
@@ -1,6 +1,7 @@
 package eu.kanade.tachiyomi.animeextension.pt.anitube.extractors
 
 import android.content.SharedPreferences
+import android.util.Base64
 import android.util.Log
 import eu.kanade.tachiyomi.animesource.model.Video
 import eu.kanade.tachiyomi.network.GET
@@ -14,6 +15,9 @@ import okhttp3.OkHttpClient
 import okhttp3.Request
 import org.jsoup.nodes.Document
 import java.net.ProtocolException
+import java.text.SimpleDateFormat
+import java.util.Calendar
+import java.util.Locale
 
 class AnitubeExtractor(
     private val headers: Headers,
@@ -30,10 +34,14 @@ class AnitubeExtractor(
 
     private fun checkVideoExists(url: String): VideoExists {
         try {
+            val newHeaders = headers.newBuilder()
+                .set("Connection", "close")
+                .build()
+
             val request = Request.Builder()
                 .head()
                 .url(url)
-                .headers(headers)
+                .headers(newHeaders)
                 .build()
 
             val response = client.newCall(request).execute()
@@ -44,6 +52,8 @@ class AnitubeExtractor(
             if (e.message?.contains("Unexpected status line") == true) {
                 return VideoExists(true, 200)
             }
+        } catch (e: Exception) {
+            Log.d(tag, "Failed to check video, error: ${e.message}")
         }
 
         return VideoExists(false, 404)
@@ -108,7 +118,7 @@ class AnitubeExtractor(
                 .body.string()
 
             val adsUrl = body.let {
-                Regex("""urlToFetch\s*=\s*['"]([^'"]+)['"]""")
+                Regex("""(?:urlToFetch|ADS_URL)\s*=\s*['"]([^'"]+)['"]""")
                     .find(it)?.groups?.get(1)?.value
                     ?: ""
             }
@@ -123,35 +133,89 @@ class AnitubeExtractor(
 
         // Try default url
         Log.e(tag, "Failed to get the ADS URL, trying the default")
-        return "https://s4.cdnpc.net/vite-bundle/main.css?version=v93"
+        return "https://widgets.outbrain.com/outbrain.js"
     }
 
-    private fun getAuthCode(serverUrl: String, thumbUrl: String, link: String): String {
-        var authCode = preferences.getString(PREF_AUTHCODE_KEY, "")!!
+    private fun getAuthCode(serverUrl: String, thumbUrl: String, link: String): String? {
+        try {
+            var authCode = preferences.getString(PREF_AUTHCODE_KEY, "")!!
 
-        if (authCode.isNotBlank()) {
-            Log.d(tag, "AuthCode found in preferences")
+            if (authCode.isNotBlank()) {
+                Log.d(tag, "AuthCode found in preferences")
 
-            val response = checkVideoExists("${serverUrl}$authCode")
+                val authArgs = Base64.decode(authCode.substringAfter("="), Base64.DEFAULT).let(::String)
+                val url = "$serverUrl?$authArgs".toHttpUrl()
 
-            if (response.exists || response.code == 500) {
-                Log.d(tag, "AuthCode is OK")
+                val serverTime =
+                    SimpleDateFormat("M/d/yyyy h:m:s a", Locale.ENGLISH).parse(
+                        url.queryParameter("server_time") ?: "",
+                    )
+
+                val calendar = Calendar.getInstance()
+                serverTime?.let { calendar.setTime(it) }
+
+                url.queryParameter("validminutes")?.toInt()
+                    ?.let { calendar.add(Calendar.MINUTE, it) }
+
+                if (Calendar.getInstance() < calendar) {
+                    Log.d(tag, "AuthCode is OK")
+                    return authCode
+                }
+                Log.d(tag, "AuthCode is invalid")
+            }
+
+            Log.d(tag, "Fetching new authCode")
+
+            val adsUrl = getAdsUrl(serverUrl, thumbUrl, link, headers)
+
+            val adsContent = client.newCall(GET(adsUrl)).execute().body.string()
+
+            val body = FormBody.Builder()
+                .add("category", "client")
+                .add("type", "premium")
+                .add("ad", adsContent)
+                .build()
+
+            val newHeaders = headers.newBuilder()
+                .set("Referer", "https://${SITE_URL.toHttpUrl().host}/")
+                .add("Accept", "*/*")
+                .add("Cache-Control", "no-cache")
+                .add("Pragma", "no-cache")
+                .add("Connection", "keep-alive")
+                .add("Sec-Fetch-Dest", "empty")
+                .add("Sec-Fetch-Mode", "cors")
+                .add("Sec-Fetch-Site", "same-site")
+                .build()
+
+            authCode =
+                client.newCall(POST(ADS_URL, headers = newHeaders, body = body))
+                    .execute()
+                    .body.string()
+                    .substringAfter("\"publicidade\"")
+                    .substringAfter('"')
+                    .substringBefore('"')
+
+            if (authCode.startsWith("?wmsAuthSign=")) {
+                Log.d(tag, "Auth code fetched successfully")
+                preferences.edit().putString(PREF_AUTHCODE_KEY, authCode).commit()
                 return authCode
             }
-            Log.d(tag, "AuthCode is invalid")
+
+            Log.e(
+                tag,
+                "Failed to fetch \"publicidade\" code, the current response: $authCode",
+            )
+        } catch (e: Exception) {
+            Log.e(tag, e.toString())
         }
 
-        Log.d(tag, "Fetching new authCode")
+        preferences.edit().putString(PREF_AUTHCODE_KEY, "").commit()
 
-        val adsUrl = getAdsUrl(serverUrl, thumbUrl, link, headers)
+        return null
+    }
 
-        val adsContent = client.newCall(GET(adsUrl)).execute().body.string()
-
-        val body = FormBody.Builder()
-            .add("category", "client")
-            .add("type", "premium")
-            .add("ad", adsContent)
-            .build()
+    private fun getVideoToken(videoUrl: String, authCode: String?): String {
+        val token = authCode ?: "undefined"
 
         val newHeaders = headers.newBuilder()
             .set("Referer", "https://${SITE_URL.toHttpUrl().host}/")
@@ -164,27 +228,10 @@ class AnitubeExtractor(
             .add("Sec-Fetch-Site", "same-site")
             .build()
 
-        val publicidade =
-            client.newCall(POST(ADS_URL, headers = newHeaders, body = body))
-                .execute()
-                .body.string()
-                .substringAfter("\"publicidade\"")
-                .substringAfter('"')
-                .substringBefore('"')
-
-        if (publicidade.isBlank()) {
-            Log.e(
-                tag,
-                "Failed to fetch \"publicidade\" code, the current response: $publicidade",
-            )
-
-            throw Exception("Por favor, abra o vídeo uma vez no navegador para liberar o IP")
-        }
-
-        authCode =
+        val videoToken =
             client.newCall(
                 GET(
-                    "$ADS_URL?token=$publicidade",
+                    "$ADS_URL?token=$token&url=$videoUrl",
                     headers = newHeaders,
                 ),
             )
@@ -194,24 +241,25 @@ class AnitubeExtractor(
                 .substringAfter('"')
                 .substringBefore('"')
 
-        if (authCode.startsWith("?")) {
-            Log.d(tag, "Auth code fetched successfully")
-            preferences.edit().putString(PREF_AUTHCODE_KEY, authCode).commit()
-        } else {
-            Log.e(
-                tag,
-                "Failed to fetch auth code, the current response: $authCode",
-            )
+        if (videoToken.startsWith("?")) {
+            Log.d(tag, "Video token fetched successfully")
+            return videoToken
         }
 
-        return authCode
+        Log.e(
+            tag,
+            "Failed to fetch video token, the current response: $videoToken",
+        )
+
+        return ""
     }
 
     fun getVideoList(doc: Document): List<Video> {
+        Log.d(tag, "Starting to fetch video list")
+
         val hasFHD = doc.selectFirst("div.abaItem:contains(FULLHD)") != null
         val serverUrl = doc.selectFirst("meta[itemprop=contentURL]")!!
             .attr("content")
-            .replace("cdn1", "cdn3")
         val thumbUrl = doc.selectFirst("meta[itemprop=thumbnailUrl]")!!
             .attr("content")
         val type = serverUrl.split("/").get(3)
@@ -224,6 +272,8 @@ class AnitubeExtractor(
             }
         } + listOf("appfullhd")
 
+        Log.d(tag, "Found ${paths.size} videos")
+
         val firstLink =
             doc.selectFirst("div.video_container > a, div.playerContainer > a")!!.attr("href")
 
@@ -233,23 +283,42 @@ class AnitubeExtractor(
             .mapIndexed { index, quality ->
                 object {
                     var path = paths[index]
-                    var url = serverUrl.replace(type, path) + authCode
+                    var url = serverUrl.replace(type, path)
                     var quality = "$quality - Anitube"
                 }
             }
             .parallelCatchingFlatMapBlocking {
-                if (!checkVideoExists(it.url).exists) {
-                    Log.d(tag, "Video not exists: ${it.url.substringBefore("?")}")
-                    return@parallelCatchingFlatMapBlocking emptyList()
+                if (!authCode.isNullOrBlank() && checkVideoExists(it.url + authCode).exists) {
+                    return@parallelCatchingFlatMapBlocking listOf(
+                        Video(
+                            it.url + authCode,
+                            it.quality,
+                            it.url + authCode,
+                            headers = headers,
+                        ),
+                    )
                 }
-                listOf(Video(it.url, it.quality, it.url, headers = headers))
+                val videoToken = getVideoToken(it.url, authCode)
+                if (videoToken.isNotBlank() && checkVideoExists(it.url + videoToken).exists) {
+                    return@parallelCatchingFlatMapBlocking listOf(
+                        Video(
+                            it.url + videoToken,
+                            it.quality,
+                            it.url + videoToken,
+                            headers = headers,
+                        ),
+                    )
+                }
+
+                Log.d(tag, "Video not exists: ${it.url.substringBefore("?")}")
+                return@parallelCatchingFlatMapBlocking emptyList()
             }
             .reversed()
     }
 
     companion object {
         private const val PREF_AUTHCODE_KEY = "authcode"
-        private const val ADS_URL = "https://ads.anitube.vip/adblock.php"
+        private const val ADS_URL = "https://ads.anitube.vip/adblockturbo.php"
         private const val SITE_URL = "https://www.anitube.vip/playerricas.php"
     }
 }
diff --git a/src/pt/flixei/AndroidManifest.xml b/src/pt/flixei/AndroidManifest.xml
deleted file mode 100644
index 6fa0bad2..00000000
--- a/src/pt/flixei/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <application>
-        <activity
-            android:name=".pt.flixei.FlixeiUrlActivity"
-            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="flixei.com"
-                    android:pathPattern="/assistir/..*/..*"
-                    android:scheme="https" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/src/pt/flixei/build.gradle b/src/pt/flixei/build.gradle
deleted file mode 100644
index efc6facf..00000000
--- a/src/pt/flixei/build.gradle
+++ /dev/null
@@ -1,12 +0,0 @@
-ext {
-    extName = 'Flixei'
-    extClass = '.Flixei'
-    extVersionCode = 8
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(":lib:streamtape-extractor"))
-    implementation(project(":lib:mixdrop-extractor"))
-}
diff --git a/src/pt/flixei/res/mipmap-hdpi/ic_launcher.png b/src/pt/flixei/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 61d07595..00000000
Binary files a/src/pt/flixei/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/flixei/res/mipmap-mdpi/ic_launcher.png b/src/pt/flixei/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 8b2f31da..00000000
Binary files a/src/pt/flixei/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/flixei/res/mipmap-xhdpi/ic_launcher.png b/src/pt/flixei/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 58738047..00000000
Binary files a/src/pt/flixei/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/flixei/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/flixei/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 0dee2e43..00000000
Binary files a/src/pt/flixei/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/flixei/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/flixei/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 678679b5..00000000
Binary files a/src/pt/flixei/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/flixei/src/eu/kanade/tachiyomi/animeextension/pt/flixei/Flixei.kt b/src/pt/flixei/src/eu/kanade/tachiyomi/animeextension/pt/flixei/Flixei.kt
deleted file mode 100644
index 1aad72bc..00000000
--- a/src/pt/flixei/src/eu/kanade/tachiyomi/animeextension/pt/flixei/Flixei.kt
+++ /dev/null
@@ -1,346 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.flixei
-
-import android.app.Application
-import androidx.preference.ListPreference
-import androidx.preference.PreferenceScreen
-import eu.kanade.tachiyomi.animeextension.pt.flixei.dto.AnimeDto
-import eu.kanade.tachiyomi.animeextension.pt.flixei.dto.ApiResultsDto
-import eu.kanade.tachiyomi.animeextension.pt.flixei.dto.EpisodeDto
-import eu.kanade.tachiyomi.animeextension.pt.flixei.dto.PlayersDto
-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.ParsedAnimeHttpSource
-import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
-import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
-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 eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-import eu.kanade.tachiyomi.util.parseAs
-import kotlinx.serialization.json.Json
-import okhttp3.MediaType.Companion.toMediaType
-import okhttp3.Request
-import okhttp3.RequestBody.Companion.toRequestBody
-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 uy.kohesive.injekt.injectLazy
-
-class Flixei : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
-
-    override val name = "Flixei"
-
-    override val baseUrl = "https://flixei.com"
-
-    override val lang = "pt-BR"
-
-    override val supportsLatest = true
-
-    private val json: Json by injectLazy()
-
-    private val preferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    // ============================== Popular ===============================
-    override fun popularAnimeRequest(page: Int): Request {
-        val body = "slider=3".toFormBody()
-        return POST("$baseUrl/includes/ajax/home.php", body = body)
-    }
-
-    override fun popularAnimeParse(response: Response): AnimesPage {
-        val results = response.parseAs<ApiResultsDto<AnimeDto>>()
-        val animes = results.items.values.map(::parseAnimeFromObject)
-        return AnimesPage(animes, false)
-    }
-
-    private fun parseAnimeFromObject(anime: AnimeDto) = SAnime.create().apply {
-        title = anime.title
-        setUrlWithoutDomain("/assistir/filme/${anime.url}/online/gratis")
-        thumbnail_url = "$baseUrl/content/movies/posterPt/185/${anime.id}.webp"
-    }
-
-    override fun popularAnimeFromElement(element: Element): SAnime {
-        throw UnsupportedOperationException()
-    }
-
-    override fun popularAnimeNextPageSelector(): String? {
-        throw UnsupportedOperationException()
-    }
-
-    override fun popularAnimeSelector(): String {
-        throw UnsupportedOperationException()
-    }
-
-    // =============================== Latest ===============================
-    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/filmes/estreia/$page")
-
-    override fun latestUpdatesSelector() = "div.generalMoviesList > a.gPoster"
-
-    override fun latestUpdatesFromElement(element: Element) = SAnime.create().apply {
-        title = element.selectFirst("div.i span")!!.text()
-        thumbnail_url = element.selectFirst("img")?.attr("src")
-        setUrlWithoutDomain(element.attr("abs:href"))
-    }
-
-    override fun latestUpdatesNextPageSelector() = "div.paginationSystem a.next"
-
-    // =============================== Search ===============================
-    override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
-        return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
-            val path = query.removePrefix(PREFIX_SEARCH)
-            client.newCall(GET("$baseUrl/assistir/$path/online/gratis"))
-                .awaitSuccess()
-                .use(::searchAnimeByPathParse)
-        } else {
-            super.getSearchAnime(page, query, filters)
-        }
-    }
-
-    private fun searchAnimeByPathParse(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 {
-        return GET("$baseUrl/pesquisar/$query")
-    }
-
-    override fun searchAnimeSelector() = latestUpdatesSelector()
-
-    override fun searchAnimeFromElement(element: Element) = latestUpdatesFromElement(element)
-
-    override fun searchAnimeNextPageSelector() = null
-
-    // =========================== Anime Details ============================
-    override fun animeDetailsParse(document: Document) = SAnime.create().apply {
-        setUrlWithoutDomain(document.location())
-        thumbnail_url = document.selectFirst("meta[property=og:image]")?.attr("content")
-        val container = document.selectFirst("div.moviePresent")!!
-        with(container) {
-            title = selectFirst("h2.tit")!!.text()
-            genre = select("div.genres > span").eachText().joinToString()
-            author = getInfo("Diretor")
-            artist = getInfo("Produtoras")
-            description = buildString {
-                selectFirst("p")?.text()?.also { append(it + "\n\n") }
-                getInfo("Título")?.also { append("Título original: $it\n") }
-                getInfo("Serie de")?.also { append("ano: $it\n") }
-                getInfo("Elenco")?.also { append("Elenco: $it\n") }
-                getInfo("Qualidade")?.also { append("Qualidade: $it\n") }
-            }
-        }
-    }
-
-    // ============================== Episodes ==============================
-    private fun getSeasonEps(seasonElement: Element): List<SEpisode> {
-        val id = seasonElement.attr("data-load-episodes")
-        val sname = seasonElement.text()
-        val body = "getEpisodes=$id".toFormBody()
-        val response = client.newCall(POST("$EMBED_WAREZ_URL/serieAjax.php", body = body)).execute()
-        val episodes = response.parseAs<ApiResultsDto<EpisodeDto>>().items.values.map {
-            SEpisode.create().apply {
-                name = "Temp $sname: Ep ${it.name}"
-                episode_number = it.name.toFloatOrNull() ?: 0F
-                url = it.id
-            }
-        }
-        return episodes
-    }
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val docUrl = response.asJsoup().selectFirst("div#playButton")!!
-            .attr("onclick")
-            .substringAfter("'")
-            .substringBefore("'")
-        return if (response.request.url.toString().contains("/serie/")) {
-            client.newCall(GET(docUrl)).execute()
-                .asJsoup()
-                .select("div#seasons div.item[data-load-episodes]")
-                .flatMap(::getSeasonEps)
-                .reversed()
-        } else {
-            listOf(
-                SEpisode.create().apply {
-                    name = "Filme"
-                    episode_number = 1F
-                    url = "$EMBED_WAREZ_URL/filme/" + docUrl.substringAfter("=")
-                },
-            )
-        }
-    }
-    override fun episodeFromElement(element: Element): SEpisode {
-        throw UnsupportedOperationException()
-    }
-
-    override fun episodeListSelector(): String {
-        throw UnsupportedOperationException()
-    }
-
-    // ============================ Video Links =============================
-    override fun videoListRequest(episode: SEpisode): Request {
-        val url = episode.url
-        return if (url.startsWith("https")) {
-            // Its an real url, maybe from a movie
-            GET(url, headers)
-        } else {
-            POST("$EMBED_WAREZ_URL/serieAjax.php", body = "getAudios=$url".toFormBody())
-        }
-    }
-
-    override fun videoListParse(response: Response): List<Video> {
-        val body = response.body.string()
-        // Pair<Language, Query>
-        val items = if (body.startsWith("{")) {
-            val data = json.decodeFromString<ApiResultsDto<PlayersDto>>(body)
-            data.items.values.flatMap {
-                val lang = if (it.audio == "1") "LEGENDADO" else "DUBLADO"
-                it.iterator().mapNotNull { (server, status) ->
-                    if (status == "3") {
-                        Pair(lang, "?id=${it.id}&sv=$server")
-                    } else {
-                        null
-                    }
-                }
-            }
-        } else {
-            val doc = response.asJsoup(body)
-            doc.select("div.selectAudioButton").flatMap {
-                val lang = it.text()
-                val id = it.attr("data-load-hosts")
-                doc.select("div[data-load-embed=$id]").map { element ->
-                    lang to "?id=$id&sv=${element.attr("data-load-embed-host")}"
-                }
-            }.ifEmpty {
-                val lang = doc.selectFirst("div.selectAudio > b")!!.text()
-                    .substringBefore("/")
-                    .uppercase()
-                val id = doc.selectFirst("*[data-load-embed]")!!.attr("data-load-embed")
-                doc.select("div.buttonLoadHost").map {
-                    lang to "?id=$id&sv=${it.attr("data-load-embed-host")}"
-                }
-            }
-        }
-        return items.parallelCatchingFlatMapBlocking(::getVideosFromItem)
-    }
-
-    private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
-    private val mixdropExtractor by lazy { MixDropExtractor(client) }
-
-    private fun getVideosFromItem(item: Pair<String, String>): List<Video> {
-        val (lang, query) = item
-        val headers = headersBuilder().set("referer", WAREZ_URL).build()
-        val hostUrl = if ("warezcdn" in query) {
-            "$WAREZ_URL/player/player.php$query"
-        } else {
-            client.newCall(GET("$WAREZ_URL/embed/getPlay.php$query", headers))
-                .execute()
-                .body.string()
-                .substringAfter("location.href=\"")
-                .substringBefore("\";")
-        }
-
-        return when (query.substringAfter("sv=")) {
-            "streamtape" -> streamtapeExtractor.videosFromUrl(hostUrl, "Streamtape($lang)")
-            "mixdrop" -> mixdropExtractor.videoFromUrl(hostUrl, lang)
-            else -> null // TODO: Add warezcdn extractor
-        }.orEmpty()
-    }
-
-    override fun videoFromElement(element: Element): Video {
-        throw UnsupportedOperationException()
-    }
-
-    override fun videoListSelector(): String {
-        throw UnsupportedOperationException()
-    }
-
-    override fun videoUrlParse(document: Document): String {
-        throw UnsupportedOperationException()
-    }
-
-    // ============================== Settings ==============================
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        ListPreference(screen.context).apply {
-            key = PREF_PLAYER_KEY
-            title = PREF_PLAYER_TITLE
-            entries = PREF_PLAYER_ARRAY
-            entryValues = PREF_PLAYER_ARRAY
-            setDefaultValue(PREF_PLAYER_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_LANGUAGE_KEY
-            title = PREF_LANGUAGE_TITLE
-            entries = PREF_LANGUAGE_ENTRIES
-            entryValues = PREF_LANGUAGE_VALUES
-            setDefaultValue(PREF_LANGUAGE_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)
-    }
-
-    // ============================= Utilities ==============================
-
-    private fun Element.getInfo(item: String) = selectFirst("*:containsOwn($item) b")?.text()
-
-    private fun String.toFormBody() = toRequestBody("application/x-www-form-urlencoded".toMediaType())
-
-    override fun List<Video>.sort(): List<Video> {
-        val player = preferences.getString(PREF_PLAYER_KEY, PREF_PLAYER_DEFAULT)!!
-        val language = preferences.getString(PREF_LANGUAGE_KEY, PREF_LANGUAGE_DEFAULT)!!
-        return sortedWith(
-            compareBy(
-                { it.quality.contains(player) },
-                { it.quality.contains(language) },
-            ),
-        ).reversed()
-    }
-
-    companion object {
-
-        const val PREFIX_SEARCH = "path:"
-
-        private const val EMBED_WAREZ_URL = "https://embed.warezcdn.net"
-        private const val WAREZ_URL = "https://warezcdn.com"
-
-        private const val PREF_PLAYER_KEY = "pref_player"
-        private const val PREF_PLAYER_DEFAULT = "MixDrop"
-        private const val PREF_PLAYER_TITLE = "Player/Server favorito"
-        private val PREF_PLAYER_ARRAY = arrayOf(
-            "MixDrop",
-            "Streamtape",
-        )
-
-        private const val PREF_LANGUAGE_KEY = "pref_language"
-        private const val PREF_LANGUAGE_DEFAULT = "LEG"
-        private const val PREF_LANGUAGE_TITLE = "Língua/tipo preferido"
-        private val PREF_LANGUAGE_ENTRIES = arrayOf("Legendado", "Dublado")
-        private val PREF_LANGUAGE_VALUES = arrayOf("LEG", "DUB")
-    }
-}
diff --git a/src/pt/flixei/src/eu/kanade/tachiyomi/animeextension/pt/flixei/FlixeiUrlActivity.kt b/src/pt/flixei/src/eu/kanade/tachiyomi/animeextension/pt/flixei/FlixeiUrlActivity.kt
deleted file mode 100644
index de531bdc..00000000
--- a/src/pt/flixei/src/eu/kanade/tachiyomi/animeextension/pt/flixei/FlixeiUrlActivity.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.flixei
-
-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://flixei.com/assistir/<type>/<item> intents
- * and redirects them to the main Aniyomi process.
- */
-class FlixeiUrlActivity : Activity() {
-
-    private val tag = javaClass.simpleName
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        val pathSegments = intent?.data?.pathSegments
-        if (pathSegments != null && pathSegments.size > 2) {
-            val type = pathSegments[1]
-            val item = pathSegments[2]
-            val mainIntent = Intent().apply {
-                action = "eu.kanade.tachiyomi.ANIMESEARCH"
-                putExtra("query", "${Flixei.PREFIX_SEARCH}$type/$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)
-    }
-}
diff --git a/src/pt/flixei/src/eu/kanade/tachiyomi/animeextension/pt/flixei/dto/FlixeiDto.kt b/src/pt/flixei/src/eu/kanade/tachiyomi/animeextension/pt/flixei/dto/FlixeiDto.kt
deleted file mode 100644
index e3849cd9..00000000
--- a/src/pt/flixei/src/eu/kanade/tachiyomi/animeextension/pt/flixei/dto/FlixeiDto.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.flixei.dto
-
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-
-@Serializable
-data class ApiResultsDto<T>(
-    @SerialName("list")
-    val items: Map<String, T>,
-)
-
-@Serializable
-data class AnimeDto(val id: String, val title: String, val url: String)
-
-@Serializable
-data class EpisodeDto(val id: String, val name: String)
-
-@Serializable
-data class PlayersDto(
-    val id: String,
-    val audio: String,
-    val mixdropStatus: String = "0",
-    val streamtapeStatus: String = "0",
-    val warezcdnStatus: String = "0",
-) {
-    operator fun iterator(): List<Pair<String, String>> {
-        return listOf(
-            "streamtape" to streamtapeStatus,
-            "mixdrop" to mixdropStatus,
-            "warezcdn" to warezcdnStatus,
-        )
-    }
-}
diff --git a/src/pt/goanimes/build.gradle b/src/pt/goanimes/build.gradle
deleted file mode 100644
index ba97b5b4..00000000
--- a/src/pt/goanimes/build.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
-ext {
-    extName = 'GoAnimes'
-    extClass = '.GoAnimes'
-    themePkg = 'dooplay'
-    baseUrl = 'https://goanimes.net'
-    overrideVersionCode = 17
-    isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(":lib:playlist-utils"))
-    implementation(project(":lib:blogger-extractor"))
-    implementation(libs.jsunpacker)
-}
\ No newline at end of file
diff --git a/src/pt/goanimes/res/mipmap-hdpi/ic_launcher.png b/src/pt/goanimes/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 28f83b4b..00000000
Binary files a/src/pt/goanimes/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/goanimes/res/mipmap-mdpi/ic_launcher.png b/src/pt/goanimes/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index e2211ca3..00000000
Binary files a/src/pt/goanimes/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/goanimes/res/mipmap-xhdpi/ic_launcher.png b/src/pt/goanimes/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 25602ee3..00000000
Binary files a/src/pt/goanimes/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/goanimes/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/goanimes/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 9a852ec0..00000000
Binary files a/src/pt/goanimes/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/goanimes/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/goanimes/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 9d4d528e..00000000
Binary files a/src/pt/goanimes/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/GoAnimes.kt b/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/GoAnimes.kt
deleted file mode 100644
index a5f5af15..00000000
--- a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/GoAnimes.kt
+++ /dev/null
@@ -1,185 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.goanimes
-
-import eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors.BloggerJWPlayerExtractor
-import eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors.GoAnimesExtractor
-import eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors.JsDecoder
-import eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors.LinkfunBypasser
-import eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors.PlaylistExtractor
-import eu.kanade.tachiyomi.animesource.model.SEpisode
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.lib.bloggerextractor.BloggerExtractor
-import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
-import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.await
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-import okhttp3.Response
-import org.jsoup.nodes.Element
-
-class GoAnimes : DooPlay(
-    "pt-BR",
-    "GoAnimes",
-    "https://goanimes.net",
-) {
-    // ============================== Popular ===============================
-    override fun popularAnimeSelector() = "div#featured-titles article.item.tvshows > div.poster"
-
-    // =============================== Latest ===============================
-    override val latestUpdatesPath = "lancamentos"
-
-    // ============================== Episodes ==============================
-    override val seasonListSelector = "div#seasons > *"
-
-    override fun getSeasonEpisodes(season: Element): List<SEpisode> {
-        // All episodes are listed under a single page
-        season.selectFirst(episodeListSelector())?.let {
-            return getSeasonEpisodesRecursive(season)
-        }
-
-        // Episodes are listed at another page
-        val url = season.attr("href")
-        return client.newCall(GET(url, headers))
-            .execute()
-            .asJsoup()
-            .let(::getSeasonEpisodes)
-    }
-
-    private val episodeListNextPageSelector = "div.pagination span.current + a:not(.arrow_pag)"
-
-    private fun getSeasonEpisodesRecursive(season: Element): List<SEpisode> {
-        var doc = season.root()
-        return buildList {
-            do {
-                if (isNotEmpty()) {
-                    doc.selectFirst(episodeListNextPageSelector)?.let {
-                        val url = it.attr("abs:href")
-                        doc = client.newCall(GET(url, headers)).execute()
-                            .asJsoup()
-                    }
-                }
-                addAll(super.getSeasonEpisodes(doc))
-            } while (doc.selectFirst(episodeListNextPageSelector) != null)
-            reversed()
-        }
-    }
-
-    // ============================ Video Links =============================
-    override val prefQualityValues = arrayOf("240p", "360p", "480p", "720p", "1080p")
-    override val prefQualityEntries = prefQualityValues
-
-    private val goanimesExtractor by lazy { GoAnimesExtractor(client, headers) }
-    private val bloggerExtractor by lazy { BloggerExtractor(client) }
-    private val linkfunBypasser by lazy { LinkfunBypasser(client) }
-    private val playlistUtils by lazy { PlaylistUtils(client, headers) }
-
-    override fun videoListParse(response: Response): List<Video> {
-        val document = response.asJsoup()
-        val players = document.select("ul#playeroptionsul li")
-        return players.parallelCatchingFlatMapBlocking(::getPlayerVideos)
-    }
-
-    private suspend fun getPlayerVideos(player: Element): List<Video> {
-        val name = player.selectFirst("span.title")!!.text()
-            .replace("FULLHD", "1080p")
-            .replace("HD", "720p")
-            .replace("SD", "480p")
-        val url = getPlayerUrl(player)
-        return when {
-            "https://gojopoolt" in url -> {
-                val headers = headers.newBuilder()
-                    .set("referer", url)
-                    .build()
-
-                val script = client.newCall(GET(url, headers)).await()
-                    .body.string()
-                    .let { JsDecoder.decodeScript(it, false).ifBlank { it } }
-
-                script.substringAfter("sources: [")
-                    .substringBefore(']')
-                    .split('{')
-                    .drop(1)
-                    .mapNotNull {
-                        val videoUrl = it.substringAfter("file: ")
-                            .substringBefore(", ")
-                            .trim('"', '\'', ' ')
-                            .ifBlank { return@mapNotNull null }
-
-                        val resolution = it.substringAfter("label: ", "")
-                            .substringAfter('"')
-                            .substringBefore('"')
-                            .ifBlank { name.split('-').last().trim() }
-
-                        val partialName = name.split('-').first().trim()
-                        return when {
-                            videoUrl.contains(".m3u8") -> {
-                                playlistUtils.extractFromHls(
-                                    videoUrl,
-                                    url,
-                                    videoNameGen = {
-                                        "$partialName - ${it.replace("Video", resolution)}"
-                                    },
-                                )
-                            }
-                            else -> listOf(Video(videoUrl, "$partialName - $resolution", videoUrl, headers))
-                        }
-                    }
-            }
-            listOf("/bloggerjwplayer", "/m3u8", "/multivideo").any { it in url } -> {
-                val script = client.newCall(GET(url)).await()
-                    .body.string()
-                    .let { JsDecoder.decodeScript(it, true).ifBlank { JsDecoder.decodeScript(it, false).ifBlank { it } } }
-                when {
-                    "/bloggerjwplayer" in url ->
-                        BloggerJWPlayerExtractor.videosFromScript(script)
-                    "/m3u8" in url ->
-                        PlaylistExtractor.videosFromScript(script)
-                    "/multivideo" in url ->
-                        script.substringAfter("attr")
-                            .substringAfter(" \"")
-                            .substringBefore('"')
-                            .let { goanimesExtractor.videosFromUrl(it, name) }
-
-                    else -> emptyList<Video>()
-                }
-            }
-            "www.blogger.com" in url -> bloggerExtractor.videosFromUrl(url, headers)
-            else -> goanimesExtractor.videosFromUrl(url, name)
-        }
-    }
-
-    private suspend fun getPlayerUrl(player: Element): String {
-        val type = player.attr("data-type")
-        val id = player.attr("data-post")
-        val num = player.attr("data-nume")
-        val url = client.newCall(GET("$baseUrl/wp-json/dooplayer/v2/$id/$type/$num"))
-            .await()
-            .body.string()
-            .substringAfter("\"embed_url\":\"")
-            .substringBefore("\",")
-            .replace("\\", "")
-
-        return when {
-            "/protetorlinks/" in url -> {
-                val link = client.newCall(GET(url)).await()
-                    .asJsoup()
-                    .selectFirst("a[href]")!!.attr("href")
-
-                client.newCall(GET(link)).await()
-                    .use(linkfunBypasser::getIframeUrl)
-            }
-            else -> url
-        }
-    }
-    // ============================= Utilities ==============================
-
-    override fun List<Video>.sort(): List<Video> {
-        val quality = preferences.getString(videoSortPrefKey, videoSortPrefDefault)!!
-        return sortedWith(
-            compareBy(
-                { it.quality.contains(quality) },
-                { Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
-            ),
-        ).reversed()
-    }
-}
diff --git a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/BloggerJWPlayerExtractor.kt b/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/BloggerJWPlayerExtractor.kt
deleted file mode 100644
index e44071a7..00000000
--- a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/BloggerJWPlayerExtractor.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors
-
-import eu.kanade.tachiyomi.animesource.model.Video
-
-object BloggerJWPlayerExtractor {
-    fun videosFromScript(script: String): List<Video> {
-        val sources = script.substringAfter("sources: [").substringBefore("],")
-
-        return sources.split("{").drop(1).map {
-            val label = it.substringAfter("label").substringAfter(":\"").substringBefore('"')
-            val videoUrl = it.substringAfter("file")
-                .substringAfter(":\"")
-                .substringBefore('"')
-                .replace("\\", "")
-            Video(videoUrl, "BloggerJWPlayer - $label", videoUrl)
-        }
-    }
-}
diff --git a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/GoAnimesExtractor.kt b/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/GoAnimesExtractor.kt
deleted file mode 100644
index b416c144..00000000
--- a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/GoAnimesExtractor.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors
-
-import android.util.Base64
-import dev.datlag.jsunpacker.JsUnpacker
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
-import eu.kanade.tachiyomi.network.GET
-import okhttp3.Headers
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.OkHttpClient
-
-class GoAnimesExtractor(private val client: OkHttpClient, private val headers: Headers) {
-    private val playlistUtils by lazy { PlaylistUtils(client, headers) }
-
-    fun videosFromUrl(url: String, name: String): List<Video> {
-        val body = client.newCall(GET(url, headers)).execute()
-            .body.string()
-
-        val decodedBody = JsUnpacker.unpackAndCombine(body)
-            ?: JsDecoder.decodeScript(body, false).takeIf(String::isNotEmpty)
-            ?: JsDecoder.decodeScript(body, true).takeIf(String::isNotEmpty)
-            ?: body
-
-        val partialName = name.split('-').first().trim()
-        val resolution = name.split('-').last().trim()
-
-        return when {
-            "/proxy/v.php" in url -> {
-                val playlistUrl = JsUnpacker.unpackAndCombine(body)
-                    ?.substringAfterLast("player(\\'", "")
-                    ?.substringBefore("\\'", "")
-                    ?.takeIf(String::isNotEmpty)
-                    ?: return emptyList()
-
-                playlistUtils.extractFromHls(
-                    playlistUrl,
-                    url,
-                    videoNameGen = { "$partialName - ${it.replace("Video", resolution)}" },
-                )
-            }
-            "/proxy/api3/" in url -> {
-                val playlistUrl = body.substringAfter("sources:", "")
-                    .substringAfter("file:", "")
-                    .substringAfter("'", "")
-                    .substringBefore("'", "")
-                    .takeIf(String::isNotEmpty)
-                    ?: return emptyList()
-
-                val fixedUrl = if (playlistUrl.contains("/aHR0")) {
-                    val encoded = playlistUrl.substringAfterLast("/").substringBefore(".")
-                    String(Base64.decode(encoded, Base64.DEFAULT))
-                } else {
-                    playlistUrl
-                }
-
-                val referer = url.toHttpUrl().queryParameter("url") ?: url
-                playlistUtils.extractFromHls(
-                    fixedUrl,
-                    referer,
-                    videoNameGen = { "$partialName - ${it.replace("Video", resolution)}" },
-                )
-            }
-            "jwplayer" in decodedBody && "sources:" in decodedBody -> {
-                val videos = PlaylistExtractor.videosFromScript(decodedBody, partialName)
-
-                if ("label:" !in decodedBody && videos.size === 1) {
-                    return playlistUtils.extractFromHls(
-                        videos[0].url,
-                        url,
-                        videoNameGen = { "$partialName - ${it.replace("Video", resolution)}" },
-                    )
-                }
-
-                videos
-            }
-            else -> emptyList()
-        }
-    }
-}
-
-private const val PLAYER_NAME = "GoAnimes"
diff --git a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/JsDecoder.kt b/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/JsDecoder.kt
deleted file mode 100644
index 22be5fe8..00000000
--- a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/JsDecoder.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors
-
-import android.util.Base64
-import kotlin.math.pow
-
-object JsDecoder {
-    private fun convertToNum(thing: String, limit: Float): Int {
-        return thing.split("")
-            .reversed()
-            .map { it.toIntOrNull() ?: 0 }
-            .reduceIndexed { index: Int, acc, num ->
-                acc + (num * limit.pow(index - 1)).toInt()
-            }
-    }
-
-    fun decodeScript(encodedString: String, magicStr: String, offset: Int, limit: Int): String {
-        val regex = "\\w".toRegex()
-        return encodedString
-            .split(magicStr[limit])
-            .dropLast(1)
-            .map { str ->
-                val replaced = regex.replace(str) { magicStr.indexOf(it.value).toString() }
-                val charInt = convertToNum(replaced, limit.toFloat()) - offset
-                Char(charInt)
-            }.joinToString("")
-    }
-
-    fun decodeScript(html: String, isB64: Boolean = true): String {
-        val script = if (isB64) {
-            html.substringAfter(";base64,")
-                .substringBefore('"')
-                .let { String(Base64.decode(it, Base64.DEFAULT)) }
-        } else {
-            html
-        }
-
-        val regex = """\}\("(\w+)",.*?"(\w+)",(\d+),(\d+),.*?\)""".toRegex()
-        return regex.find(script)
-            ?.run {
-                decodeScript(
-                    groupValues[1], // encoded data
-                    groupValues[2], // magic string
-                    groupValues[3].toIntOrNull() ?: 0, // offset
-                    groupValues[4].toIntOrNull() ?: 0, // limit
-                )
-            } ?: ""
-    }
-}
diff --git a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/LinkfunBypasser.kt b/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/LinkfunBypasser.kt
deleted file mode 100644
index 46bfec81..00000000
--- a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/LinkfunBypasser.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors
-
-import android.util.Base64
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.util.asJsoup
-import okhttp3.FormBody
-import okhttp3.Headers
-import okhttp3.OkHttpClient
-import okhttp3.Response
-
-class LinkfunBypasser(private val client: OkHttpClient) {
-    fun getIframeUrl(page: Response): String {
-        val docString = page.body.string()
-        val document = if (docString.startsWith("<script")) {
-            page.asJsoup(decodeAtob(docString))
-        } else { page.asJsoup(docString) }
-
-        val newHeaders = Headers.headersOf("Referer", document.location())
-
-        val iframe = document.selectFirst("iframe[src]")
-
-        return if (iframe != null) {
-            iframe.attr("src")
-        } else {
-            val formBody = FormBody.Builder().apply {
-                document.select("input[name]").forEach {
-                    add(it.attr("name"), it.attr("value"))
-                }
-            }.build()
-
-            val formUrl = document.selectFirst("form")!!.attr("action")
-            client.newCall(POST(formUrl, newHeaders, formBody))
-                .execute()
-                .let(::getIframeUrl)
-        }
-    }
-
-    companion object {
-        fun decodeAtob(html: String): String {
-            val atobContent = html.substringAfter("atob(\"").substringBefore("\"));")
-            val hexAtob = atobContent.replace("\\x", "").decodeHex()
-            val decoded = Base64.decode(hexAtob, Base64.DEFAULT)
-            return String(decoded)
-        }
-
-        // Stolen from AnimixPlay(EN) / GogoCdnExtractor
-        private fun String.decodeHex(): ByteArray {
-            check(length % 2 == 0) { "Must have an even length" }
-            return chunked(2)
-                .map { it.toInt(16).toByte() }
-                .toByteArray()
-        }
-    }
-}
diff --git a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/PlaylistExtractor.kt b/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/PlaylistExtractor.kt
deleted file mode 100644
index 79c13f85..00000000
--- a/src/pt/goanimes/src/eu/kanade/tachiyomi/animeextension/pt/goanimes/extractors/PlaylistExtractor.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.goanimes.extractors
-
-import eu.kanade.tachiyomi.animesource.model.Video
-
-object PlaylistExtractor {
-    fun videosFromScript(script: String, prefix: String = "Playlist"): List<Video> {
-        val sources = script.substringAfter("sources: [").substringBefore("],")
-
-        return sources.split("{").drop(1).mapNotNull { source ->
-            val url = source.substringAfter("file:")
-                .substringAfter('"', "")
-                .substringBefore('"', "")
-                .takeIf(String::isNotEmpty)
-                ?: source.substringAfter("file:")
-                    .substringAfter("'", "")
-                    .substringBefore("'", "")
-                    .takeIf(String::isNotEmpty)
-
-            if (url.isNullOrBlank()) {
-                return@mapNotNull null
-            }
-
-            val label = source.substringAfter("label:").substringAfter('"').substringBefore('"')
-                .replace("FHD", "1080p")
-                .replace("HD", "720p")
-                .replace("SD", "480p")
-            Video(url, "$prefix - $label", url)
-        }
-    }
-}
diff --git a/src/pt/hinatasoul/AndroidManifest.xml b/src/pt/hinatasoul/AndroidManifest.xml
deleted file mode 100644
index 9328bbdf..00000000
--- a/src/pt/hinatasoul/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <application>
-        <activity
-            android:name=".pt.hinatasoul.HSUrlActivity"
-            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.hinatasoul.com"
-                    android:pathPattern="/animes/..*"
-                    android:scheme="https" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/src/pt/hinatasoul/build.gradle b/src/pt/hinatasoul/build.gradle
deleted file mode 100644
index 084d175a..00000000
--- a/src/pt/hinatasoul/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-ext {
-    extName = 'Hinata Soul'
-    extClass = '.HinataSoul'
-    extVersionCode = 9
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/pt/hinatasoul/res/mipmap-hdpi/ic_launcher.png b/src/pt/hinatasoul/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index d311d948..00000000
Binary files a/src/pt/hinatasoul/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/hinatasoul/res/mipmap-mdpi/ic_launcher.png b/src/pt/hinatasoul/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 4aa0b17a..00000000
Binary files a/src/pt/hinatasoul/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/hinatasoul/res/mipmap-xhdpi/ic_launcher.png b/src/pt/hinatasoul/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 87fed1ca..00000000
Binary files a/src/pt/hinatasoul/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/hinatasoul/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/hinatasoul/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index d480b746..00000000
Binary files a/src/pt/hinatasoul/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/hinatasoul/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/hinatasoul/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b6dc907a..00000000
Binary files a/src/pt/hinatasoul/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HSUrlActivity.kt b/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HSUrlActivity.kt
deleted file mode 100644
index c3d95336..00000000
--- a/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HSUrlActivity.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.hinatasoul
-
-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.hinatasoul.com/animes/<slug> intents
- * and redirects them to the main Aniyomi process.
- */
-class HSUrlActivity : Activity() {
-
-    private val tag = "HSUrlActivity"
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        val pathSegments = intent?.data?.pathSegments
-        if (pathSegments != null && pathSegments.size > 1) {
-            val slug = pathSegments[1]
-            val searchQuery = HinataSoul.PREFIX_SEARCH + slug
-            val mainIntent = Intent().apply {
-                action = "eu.kanade.tachiyomi.ANIMESEARCH"
-                putExtra("query", searchQuery)
-                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)
-    }
-}
diff --git a/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HinataSoul.kt b/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HinataSoul.kt
deleted file mode 100644
index 3d636c45..00000000
--- a/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HinataSoul.kt
+++ /dev/null
@@ -1,308 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.hinatasoul
-
-import android.app.Application
-import androidx.preference.EditTextPreference
-import androidx.preference.ListPreference
-import androidx.preference.PreferenceScreen
-import eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors.HinataSoulDownloadExtractor
-import eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors.HinataSoulExtractor
-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.ParsedAnimeHttpSource
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.awaitSuccess
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-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 java.text.SimpleDateFormat
-import java.util.Locale
-
-class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
-
-    override val name = "Hinata Soul"
-
-    override val baseUrl = "https://www.hinatasoul.com"
-
-    override val lang = "pt-BR"
-
-    override val supportsLatest = true
-
-    private val preferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl)
-
-    // ============================== Popular ===============================
-    override fun popularAnimeSelector() = "div.FsssItem:contains(Mais Vistos) > a"
-
-    override fun popularAnimeRequest(page: Int) = GET(baseUrl)
-
-    override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
-        setUrlWithoutDomain(element.attr("href"))
-        title = element.text()
-    }
-
-    override fun popularAnimeNextPageSelector() = null
-
-    // =============================== Latest ===============================
-    override fun latestUpdatesRequest(page: Int) = GET(baseUrl)
-
-    override fun latestUpdatesSelector() =
-        "div.tituloContainer:contains(lançamento) + div.epiContainer a"
-
-    override fun latestUpdatesFromElement(element: Element) = SAnime.create().apply {
-        setUrlWithoutDomain(element.attr("href"))
-        val img = element.selectFirst("img")!!
-        thumbnail_url = img.attr("src")
-        title = img.attr("alt")
-    }
-
-    override fun latestUpdatesNextPageSelector() = null
-
-    // =============================== Search ===============================
-    override suspend fun getSearchAnime(
-        page: Int,
-        query: String,
-        filters: AnimeFilterList,
-    ): AnimesPage {
-        return if (query.startsWith(PREFIX_SEARCH)) {
-            val slug = query.removePrefix(PREFIX_SEARCH)
-            client.newCall(GET("$baseUrl/animes/$slug"))
-                .awaitSuccess()
-                .use(::searchAnimeBySlugParse)
-        } else {
-            super.getSearchAnime(page, query, filters)
-        }
-    }
-
-    private fun searchAnimeBySlugParse(response: Response): AnimesPage {
-        val details = animeDetailsParse(response).apply {
-            setUrlWithoutDomain(response.request.url.toString())
-            initialized = true
-        }
-
-        return AnimesPage(listOf(details), false)
-    }
-
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) =
-        GET("$baseUrl/busca?busca=$query&page=$page")
-
-    override fun searchAnimeSelector() = episodeListSelector()
-
-    override fun searchAnimeNextPageSelector() = null
-
-    override fun searchAnimeFromElement(element: Element) = SAnime.create().apply {
-        setUrlWithoutDomain(element.attr("href"))
-        thumbnail_url = element.selectFirst("img")?.attr("src")
-        title = element.selectFirst("div.ultimosAnimesHomeItemInfosNome")!!.text()
-    }
-
-    override fun searchAnimeParse(response: Response): AnimesPage {
-        val document = response.asJsoup()
-        val animes = document.select(searchAnimeSelector()).map(::searchAnimeFromElement)
-        val hasNext = hasNextPage(document)
-        return AnimesPage(animes, hasNext)
-    }
-
-    // =========================== Anime Details ============================
-    override fun animeDetailsParse(document: Document) = SAnime.create().apply {
-        val doc = getRealDoc(document)
-        setUrlWithoutDomain(doc.location())
-        val infos = doc.selectFirst("div.aniInfosSingle")!!
-        val img = infos.selectFirst("img")!!
-        thumbnail_url = img.attr("src")
-        title = img.attr("alt")
-        genre = infos.select("div.aniInfosSingleGeneros > span")
-            .eachText()
-            .joinToString()
-
-        author = infos.getInfo("AUTOR")
-        artist = infos.getInfo("ESTÚDIO")
-        status = parseStatus(infos.selectFirst("div.anime_status")!!)
-
-        description = buildString {
-            append(infos.selectFirst("div.aniInfosSingleSinopse > p")!!.text() + "\n")
-            infos.getInfo("Título")?.also { append("\nTítulos Alternativos: $it") }
-            infos.selectFirst("div.aniInfosSingleNumsItem:contains(Ano)")?.also {
-                append("\nAno: ${it.ownText()}")
-            }
-            infos.getInfo("Temporada")?.also { append("\nTemporada: $it") }
-        }
-    }
-
-    // ============================== Episodes ==============================
-    override fun episodeListSelector() = "div.aniContainer a"
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        var doc = getRealDoc(response.asJsoup())
-        val totalEpisodes = buildList {
-            do {
-                if (isNotEmpty()) {
-                    val url = doc.selectFirst("div.mwidth > a:containsOwn(»)")!!.absUrl("href")
-                    doc = client.newCall(GET(url, headers)).execute().asJsoup()
-                }
-                doc.select(episodeListSelector())
-                    .map(::episodeFromElement)
-                    .also(::addAll)
-            } while (hasNextPage(doc))
-            reverse()
-        }
-        return totalEpisodes
-    }
-
-    override fun episodeFromElement(element: Element) = SEpisode.create().apply {
-        val title = element.attr("title")
-        setUrlWithoutDomain(element.attr("href"))
-        name = title
-        episode_number =
-            title.substringBeforeLast(" - FINAL").substringAfterLast(" ").toFloatOrNull() ?: 0F
-        date_upload = element.selectFirst("div.lancaster_episodio_info_data")!!
-            .text()
-            .toDate()
-    }
-
-    // ============================ Video Links =============================
-    private val hinataExtractor by lazy { HinataSoulExtractor(headers, client, preferences) }
-    private val downloadExtractor by lazy { HinataSoulDownloadExtractor(headers, client) }
-
-    override fun videoListParse(response: Response): List<Video> {
-        val document = response.asJsoup()
-
-        val links = mutableListOf(document.location())
-
-        val downloadsLinks = document.select("div.reportaBox .reportContent > a")
-
-        downloadsLinks.forEach {
-            it.attr("href")?.let {
-                links.add(it)
-            }
-        }
-
-        val epName = document.selectFirst("meta[itemprop=name]")!!.attr("content")
-
-        return links.parallelCatchingFlatMapBlocking { url ->
-            when {
-                url.contains("file4go.net") -> {
-                    val quality =
-                        downloadsLinks.first { it.attr("href") == url }
-                            .textNodes().first().toString()
-                            .trim().replace(" ", "")
-
-                    downloadExtractor.videosFromUrl(url, epName, quality)
-                }
-
-                else -> hinataExtractor.getVideoList(document)
-            }
-        }
-    }
-
-    override fun videoListSelector() = throw UnsupportedOperationException()
-    override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
-    override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
-
-    // ============================== Settings ==============================
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        ListPreference(screen.context).apply {
-            key = PREF_QUALITY_KEY
-            title = PREF_QUALITY_TITLE
-            entries = PREF_QUALITY_VALUES
-            entryValues = PREF_QUALITY_VALUES
-            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)
-
-        // Auth Code
-        EditTextPreference(screen.context).apply {
-            key = PREF_AUTHCODE_KEY
-            title = "Auth Code"
-            setDefaultValue(PREF_AUTHCODE_DEFAULT)
-            summary = PREF_AUTHCODE_SUMMARY
-
-            setOnPreferenceChangeListener { _, newValue ->
-                runCatching {
-                    val value = (newValue as String).trim().ifBlank { PREF_AUTHCODE_DEFAULT }
-                    preferences.edit().putString(key, value).commit()
-                }.getOrDefault(false)
-            }
-        }.also(screen::addPreference)
-    }
-
-    // ============================= Utilities ==============================
-    private fun parseStatus(element: Element): Int {
-        return when {
-            element.hasClass("completed") -> SAnime.COMPLETED
-            element.hasClass("airing") -> SAnime.ONGOING
-            else -> SAnime.UNKNOWN
-        }
-    }
-
-    private fun hasNextPage(doc: Document): Boolean {
-        val currentUrl = doc.location()
-        val nextUrl = doc.selectFirst("a:contains(»)")!!.attr("href")
-        val endings = listOf("/1", "page=1")
-        return endings.none(nextUrl::endsWith) && currentUrl != nextUrl
-    }
-
-    private val animeMenuSelector = "div.controlesBoxItem > a:has(i.iconLista)"
-    private fun getRealDoc(document: Document): Document {
-        if (!document.location().contains("/videos/")) {
-            return document
-        }
-
-        return document.selectFirst(animeMenuSelector)?.let {
-            client.newCall(GET(it.attr("href"), headers)).execute()
-                .asJsoup()
-        } ?: document
-    }
-
-    private fun Element.getInfo(key: String): String? {
-        return selectFirst("div.aniInfosSingleInfoItem:contains($key) span")
-            ?.text()
-            ?.trim()
-    }
-
-    private fun String.toDate(): Long {
-        return runCatching { DATE_FORMATTER.parse(trim())?.time }
-            .getOrNull() ?: 0L
-    }
-
-    override fun List<Video>.sort(): List<Video> {
-        val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
-        return sortedWith(
-            compareBy<Video>(
-                { it.quality.startsWith(quality) },
-                { PREF_QUALITY_VALUES.indexOf(it.quality.substringBefore(" ")) },
-            ).thenByDescending { it.quality },
-        ).reversed()
-    }
-
-    companion object {
-        private val DATE_FORMATTER by lazy {
-            SimpleDateFormat("dd/MM/yyyy à's' HH:mm", Locale.ENGLISH)
-        }
-
-        const val PREFIX_SEARCH = "slug:"
-
-        private const val PREF_AUTHCODE_KEY = "authcode"
-        private const val PREF_AUTHCODE_SUMMARY = "Código de Autenticação"
-        private const val PREF_AUTHCODE_DEFAULT = ""
-        private const val PREF_QUALITY_KEY = "preferred_quality"
-        private const val PREF_QUALITY_TITLE = "Qualidade preferida"
-        private const val PREF_QUALITY_DEFAULT = "FULLHD"
-        private val PREF_QUALITY_VALUES = arrayOf("SD", "HD", "FULLHD")
-    }
-}
diff --git a/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/extractors/HinataSoulDownloadExtractor.kt b/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/extractors/HinataSoulDownloadExtractor.kt
deleted file mode 100644
index 71b47df9..00000000
--- a/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/extractors/HinataSoulDownloadExtractor.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors
-
-import android.util.Log
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelMapNotNullBlocking
-import okhttp3.FormBody
-import okhttp3.Headers
-import okhttp3.OkHttpClient
-
-class HinataSoulDownloadExtractor(
-    private val headers: Headers,
-    private val client: OkHttpClient,
-) {
-
-    private val qualities = listOf("SD", "HD", "FULLHD")
-    private val tag by lazy { javaClass.simpleName }
-
-    private fun videosFromFile4Go(url: String, quality: String): Video? {
-        Log.d(tag, "Checking download for $url")
-        val docDownload = client.newCall(GET(url)).execute().asJsoup()
-
-        val form =
-            docDownload.selectFirst("button.download")?.closest("form")
-
-        if (form == null) {
-            Log.d(tag, "Download form not found for $url")
-            return null
-        }
-
-        val body = FormBody.Builder().apply {
-            form.select("input[name]").forEach {
-                add(it.attr("name"), it.attr("value"))
-            }
-        }.build()
-
-        val postUrl = form.attr("action")
-
-        val postHeaders = headers.newBuilder()
-            .set("Referer", url)
-            .build()
-
-        val docFinal =
-            client.newCall(POST(postUrl, headers = postHeaders, body = body))
-                .execute().asJsoup()
-
-        val videoUrl = docFinal.selectFirst("a.novobotao.download")?.attr("href")
-
-        if (videoUrl == null) {
-            Log.d(tag, "Download link not found for $url")
-            return null
-        }
-
-        return Video(videoUrl, "$quality - File4Go", videoUrl)
-    }
-
-    private fun videosFromDownloadPage(url: String, epName: String): List<Video> {
-        Log.d(tag, "Extracting videos links for URL: $url")
-        val docDownload = client.newCall(GET(url)).execute().asJsoup()
-
-        val row = docDownload.select("table.downloadpag_episodios tr").firstOrNull {
-            it.text().contains(epName)
-        }
-
-        if (row == null) {
-            Log.d(tag, "Episode $epName not found in download page")
-            return emptyList()
-        }
-
-        val links = row.select("td").mapIndexedNotNull { index, el ->
-            val link = el.selectFirst("a") ?: return@mapIndexedNotNull null
-
-            object {
-                var quality = qualities.get(index - 1)
-                var url = link.attr("href")
-            }
-        }
-
-        Log.d(tag, "Found ${links.size} links for $epName")
-
-        return links.parallelMapNotNullBlocking {
-            if (!it.url.contains("file4go.net")) {
-                return@parallelMapNotNullBlocking null
-            }
-            videosFromFile4Go(it.url, it.quality)
-        }.reversed()
-    }
-
-    fun videosFromUrl(url: String, epName: String, quality: String = "Default"): List<Video> {
-        if (url.contains("file4go.net")) {
-            return listOfNotNull(videosFromFile4Go(url, quality))
-        }
-
-        return videosFromDownloadPage(url, epName)
-    }
-}
diff --git a/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/extractors/HinataSoulExtractor.kt b/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/extractors/HinataSoulExtractor.kt
deleted file mode 100644
index abb6bc8d..00000000
--- a/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/extractors/HinataSoulExtractor.kt
+++ /dev/null
@@ -1,243 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors
-
-import android.content.SharedPreferences
-import android.util.Log
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelMapNotNullBlocking
-import okhttp3.FormBody
-import okhttp3.Headers
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import org.jsoup.nodes.Document
-import java.net.ProtocolException
-
-class HinataSoulExtractor(
-    private val headers: Headers,
-    private val client: OkHttpClient,
-    private val preferences: SharedPreferences,
-) {
-
-    private val tag by lazy { javaClass.simpleName }
-
-    private data class VideoExists(
-        val exists: Boolean,
-        val code: Int,
-    )
-
-    private fun checkVideoExists(url: String): VideoExists {
-        try {
-            val request = Request.Builder()
-                .head()
-                .url(url)
-                .headers(headers)
-                .build()
-
-            val response = client.newCall(request).execute()
-
-            return VideoExists(response.isSuccessful, response.code)
-        } catch (e: ProtocolException) {
-            // There are a bug in the response that sometimes that the content is without headers
-            if (e.message?.contains("Unexpected status line") == true) {
-                return VideoExists(true, 200)
-            }
-        }
-
-        return VideoExists(false, 404)
-    }
-
-    private fun getAdsUrl(
-        serverUrl: String,
-        thumbUrl: String,
-        link: String,
-        linkHeaders: Headers,
-    ): String {
-        val videoName = serverUrl.split('/').last()
-
-        val finalLink =
-            if (link.startsWith("//")) {
-                "https:$link"
-            } else {
-                link
-            }
-        Log.d(tag, "Accessing the link $finalLink")
-        val response = client.newCall(GET(finalLink, headers = linkHeaders)).execute()
-        val docLink = response.asJsoup()
-
-        val refresh = docLink.selectFirst("meta[http-equiv=refresh]")?.attr("content")
-
-        if (!refresh.isNullOrBlank()) {
-            val newLink = refresh.substringAfter("=")
-            val newHeaders = linkHeaders.newBuilder().set("Referer", finalLink).build()
-            Log.d(tag, "Following link redirection to $newLink")
-
-            return getAdsUrl(serverUrl, thumbUrl, newLink, newHeaders)
-        }
-
-        val referer: String = docLink.location() ?: link
-
-        Log.d(tag, "Final URL: $referer")
-        Log.d(tag, "Fetching ADS URL")
-
-        val newHeaders = linkHeaders.newBuilder().set("Referer", "https://${referer.toHttpUrl().host}/").build()
-
-        try {
-            val now = System.currentTimeMillis()
-            val body = client.newCall(
-                GET(
-                    "$SITE_URL?name=apphd/$videoName&img=$thumbUrl&pais=pais=BR&time=$now&url=$serverUrl",
-                    headers = newHeaders,
-                ),
-            )
-                .execute()
-                .body.string()
-
-            val adsUrl = body.let {
-                Regex("""ADS_URL\s*=\s*['"]([^'"]+)['"]""")
-                    .find(it)?.groups?.get(1)?.value
-                    ?: ""
-            }
-
-            if (adsUrl.startsWith("http")) {
-                Log.d(tag, "ADS URL: $adsUrl")
-                return adsUrl
-            }
-        } catch (e: Exception) {
-            Log.e(tag, e.toString())
-        }
-
-        // Try default url
-        Log.e(tag, "Failed to get the ADS URL, trying the default")
-        return "https://www.popads.net/js/adblock.js"
-    }
-
-    private fun getAuthCode(serverUrl: String, thumbUrl: String, link: String): String {
-        var authCode = preferences.getString(PREF_AUTHCODE_KEY, "")!!
-
-        if (authCode.isNotBlank()) {
-            Log.d(tag, "AuthCode found in preferences")
-
-            val response = checkVideoExists("${serverUrl}$authCode")
-
-            if (response.exists || response.code == 500) {
-                Log.d(tag, "AuthCode is OK")
-                return authCode
-            }
-            Log.d(tag, "AuthCode is invalid")
-        }
-
-        Log.d(tag, "Fetching new authCode")
-
-        val adsUrl = getAdsUrl(serverUrl, thumbUrl, link, headers)
-
-        val adsContent = client.newCall(GET(adsUrl)).execute().body.string()
-
-        val body = FormBody.Builder()
-            .add("category", "client")
-            .add("type", "premium")
-            .add("ad", adsContent)
-            .build()
-
-        val newHeaders = headers.newBuilder()
-            .set("Referer", "https://${SITE_URL.toHttpUrl().host}/")
-            .add("Accept", "*/*")
-            .add("Cache-Control", "no-cache")
-            .add("Pragma", "no-cache")
-            .add("Connection", "keep-alive")
-            .add("Sec-Fetch-Dest", "empty")
-            .add("Sec-Fetch-Mode", "cors")
-            .add("Sec-Fetch-Site", "same-site")
-            .build()
-
-        val publicidade =
-            client.newCall(POST("$ADS_URL/", headers = newHeaders, body = body))
-                .execute()
-                .body.string()
-                .substringAfter("\"publicidade\"")
-                .substringAfter('"')
-                .substringBefore('"')
-
-        if (publicidade.isBlank()) {
-            Log.e(
-                tag,
-                "Failed to fetch \"publicidade\" code, the current response: $publicidade",
-            )
-
-            throw Exception("Por favor, abra o vídeo uma vez no navegador para liberar o IP")
-        }
-
-        authCode =
-            client.newCall(
-                GET(
-                    "$ADS_URL/?token=$publicidade",
-                    headers = newHeaders,
-                ),
-            )
-                .execute()
-                .body.string()
-                .substringAfter("\"publicidade\"")
-                .substringAfter('"')
-                .substringBefore('"')
-
-        if (authCode.startsWith("?")) {
-            Log.d(tag, "Auth code fetched successfully")
-            preferences.edit().putString(PREF_AUTHCODE_KEY, authCode).commit()
-        } else {
-            Log.e(
-                tag,
-                "Failed to fetch auth code, the current response: $authCode",
-            )
-        }
-
-        return authCode
-    }
-
-    fun getVideoList(doc: Document): List<Video> {
-        val hasFHD = doc.selectFirst("div.abaItem:contains(FULLHD)") != null
-        val serverUrl = doc.selectFirst("meta[itemprop=contentURL]")!!
-            .attr("content")
-            .replace("cdn1", "cdn3")
-        val thumbUrl = doc.selectFirst("meta[itemprop=thumbnailUrl]")!!
-            .attr("content")
-        val type = serverUrl.split("/").get(3)
-        val qualities = listOfNotNull("SD", "HD", if (hasFHD) "FULLHD" else null)
-        val paths = listOf("appsd", "apphd").let {
-            if (type.endsWith("2")) {
-                it.map { path -> path + "2" }
-            } else {
-                it
-            }
-        } + listOf("appfullhd")
-
-        val firstLink =
-            doc.selectFirst("div.video_container > a, div.playerContainer > a")!!.attr("href")
-
-        val authCode = getAuthCode(serverUrl, thumbUrl, firstLink)
-
-        return qualities
-            .mapIndexed { index, quality ->
-                object {
-                    var path = paths[index]
-                    var url = serverUrl.replace(type, path) + authCode
-                    var quality = "$quality - Anitube"
-                }
-            }
-            .parallelMapNotNullBlocking {
-                if (!checkVideoExists(it.url).exists) {
-                    Log.d(tag, "Video not exists: ${it.url.substringBefore("?")}")
-                    return@parallelMapNotNullBlocking null
-                }
-                Video(it.url, it.quality, it.url, headers = headers)
-            }
-            .reversed()
-    }
-
-    companion object {
-        private const val PREF_AUTHCODE_KEY = "authcode"
-        private const val ADS_URL = "https://ads.anitube.vip"
-        private const val SITE_URL = "https://www.hinatasoul.com/luffy.php"
-    }
-}
diff --git a/src/pt/listadeanimes/build.gradle b/src/pt/listadeanimes/build.gradle
deleted file mode 100644
index 6ac76a1d..00000000
--- a/src/pt/listadeanimes/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-ext {
-    extName = 'Lista de Animes'
-    extClass = '.ListaDeAnimes'
-    extVersionCode = 2
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/pt/listadeanimes/res/mipmap-hdpi/ic_launcher.png b/src/pt/listadeanimes/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 5cee6385..00000000
Binary files a/src/pt/listadeanimes/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/listadeanimes/res/mipmap-mdpi/ic_launcher.png b/src/pt/listadeanimes/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index a09f0d88..00000000
Binary files a/src/pt/listadeanimes/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/listadeanimes/res/mipmap-xhdpi/ic_launcher.png b/src/pt/listadeanimes/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index fe66479c..00000000
Binary files a/src/pt/listadeanimes/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/listadeanimes/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/listadeanimes/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 4d18c5d5..00000000
Binary files a/src/pt/listadeanimes/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/listadeanimes/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/listadeanimes/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 91b2c05b..00000000
Binary files a/src/pt/listadeanimes/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/listadeanimes/src/eu/kanade/tachiyomi/animeextension/pt/listadeanimes/ListaDeAnimes.kt b/src/pt/listadeanimes/src/eu/kanade/tachiyomi/animeextension/pt/listadeanimes/ListaDeAnimes.kt
deleted file mode 100644
index b4b3b04f..00000000
--- a/src/pt/listadeanimes/src/eu/kanade/tachiyomi/animeextension/pt/listadeanimes/ListaDeAnimes.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.listadeanimes
-
-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 okhttp3.Request
-import okhttp3.Response
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-
-class ListaDeAnimes : ParsedAnimeHttpSource() {
-    override val name = "Lista de Animes"
-
-    override val baseUrl = "https://www.listadeanimes.com"
-
-    override val lang = "pt-BR"
-
-    override val supportsLatest = false
-
-    override fun headersBuilder() = super.headersBuilder()
-        .add("Referer", baseUrl)
-
-    // ============================== Popular ===============================
-    override fun popularAnimeRequest(page: Int) = GET("$baseUrl/page/$page")
-
-    override fun popularAnimeSelector() = "article.post.excerpt > div.capa:not(:has(a[href=$baseUrl/anime-lista-online]))"
-
-    override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
-        setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
-        val img = element.selectFirst("img")!!
-        title = titleCase(img.attr("title").substringBefore(" todos os episódios"))
-        thumbnail_url = img.attr("data-src")
-    }
-
-    override fun popularAnimeNextPageSelector() = "a.next.page-numbers"
-
-    // =============================== 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() = throw UnsupportedOperationException()
-
-    // =============================== Search ===============================
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = GET("$baseUrl/page/$page?s=$query")
-    override fun searchAnimeParse(response: Response) = popularAnimeParse(response)
-    override fun searchAnimeSelector() = popularAnimeSelector()
-    override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
-    override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector()
-
-    // =========================== Anime Details ============================
-    override fun animeDetailsParse(document: Document) = SAnime.create().apply {
-        setUrlWithoutDomain(document.location())
-        val titleText = document.selectFirst("h1.title.single-title")!!.text()
-        title = titleCase(titleText.substringBefore(" todos os episódios"))
-        thumbnail_url = document.selectFirst("img.aligncenter.size-full")?.attr("src")
-        val infos = document.selectFirst("div#content.post-single-content > center")
-        val infosText = infos?.run {
-            html()
-                .replace("<br>", "\n")
-                .replace("<b>", "")
-                .replace("</b>", "")
-        }?.let { "\n\n$it" }.orEmpty()
-
-        val sinopse = document.selectFirst("div#content > *:contains(Sinopse)")?.nextElementSibling()
-        description = (sinopse?.text() ?: "Sem sinopse.") + infosText
-        genre = document.select("a[rel=tag]").joinToString { it.text() }
-    }
-
-    // ============================== Episodes ==============================
-    override fun episodeListSelector() = "div.videos > ul"
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val doc = response.asJsoup()
-        return doc.select("div.videos > ul > li:gt(0)")
-            .map(::episodeFromElement)
-            .reversed()
-    }
-    override fun episodeFromElement(element: Element): SEpisode {
-        return SEpisode.create().apply {
-            episode_number = runCatching {
-                element.selectFirst("string")!!
-                    .text()
-                    .substringAfter(" ")
-                    .toFloat()
-            }.getOrDefault(0F)
-            name = element.text().substringAfter("► ")
-            url = element.attr("id")
-        }
-    }
-
-    // ============================ Video Links =============================
-    override suspend fun getVideoList(episode: SEpisode): List<Video> {
-        return listOf(Video(episode.url, episode.name, episode.url))
-    }
-
-    override fun videoListSelector() = throw UnsupportedOperationException()
-    override fun videoListRequest(episode: SEpisode) = throw UnsupportedOperationException()
-    override fun videoListParse(response: Response) = throw UnsupportedOperationException()
-    override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
-    override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
-
-    // ============================= Utilities ==============================
-    private fun titleCase(str: String): String {
-        return str.split(' ')
-            .map { it.replaceFirstChar(Char::uppercase) }
-            .joinToString(" ")
-    }
-}
diff --git a/src/pt/megaflix/AndroidManifest.xml b/src/pt/megaflix/AndroidManifest.xml
deleted file mode 100644
index c7f04aa4..00000000
--- a/src/pt/megaflix/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <application>
-        <activity
-            android:name=".pt.megaflix.MegaflixUrlActivity"
-            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="megaflix.co"
-                    android:pathPattern="/..*/..*/"
-                    android:scheme="https" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/src/pt/megaflix/build.gradle b/src/pt/megaflix/build.gradle
deleted file mode 100644
index a5713572..00000000
--- a/src/pt/megaflix/build.gradle
+++ /dev/null
@@ -1,20 +0,0 @@
-ext {
-    extName = 'Megaflix'
-    extClass = '.Megaflix'
-    extVersionCode = 24
-    isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(':lib:mixdrop-extractor'))
-    implementation(project(":lib:streamtape-extractor"))
-    implementation(project(":lib:voe-extractor"))
-    implementation(project(":lib:filemoon-extractor"))
-    implementation(project(":lib:vidhide-extractor"))
-    implementation(project(":lib:streamwish-extractor"))
-    implementation(project(":lib:playlist-utils"))
-    // for mixdrop and megaflix
-    implementation(libs.jsunpacker)
-}
diff --git a/src/pt/megaflix/res/mipmap-hdpi/ic_launcher.png b/src/pt/megaflix/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index bf99313a..00000000
Binary files a/src/pt/megaflix/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/megaflix/res/mipmap-mdpi/ic_launcher.png b/src/pt/megaflix/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 0bf5f9da..00000000
Binary files a/src/pt/megaflix/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/megaflix/res/mipmap-xhdpi/ic_launcher.png b/src/pt/megaflix/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 6ea93116..00000000
Binary files a/src/pt/megaflix/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/megaflix/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/megaflix/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index c441ac81..00000000
Binary files a/src/pt/megaflix/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/megaflix/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/megaflix/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 524bfd1d..00000000
Binary files a/src/pt/megaflix/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/Megaflix.kt b/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/Megaflix.kt
deleted file mode 100644
index e64a0785..00000000
--- a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/Megaflix.kt
+++ /dev/null
@@ -1,317 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.megaflix
-
-import android.app.Application
-import androidx.preference.ListPreference
-import androidx.preference.PreferenceScreen
-import eu.kanade.tachiyomi.animeextension.pt.megaflix.extractors.MegaflixExtractor
-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.ParsedAnimeHttpSource
-import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
-import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
-import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
-import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
-import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
-import eu.kanade.tachiyomi.lib.vidhideextractor.VidHideExtractor
-import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.await
-import eu.kanade.tachiyomi.network.awaitSuccess
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-import eu.kanade.tachiyomi.util.parallelFlatMapBlocking
-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 Megaflix : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
-
-    override val name = "Megaflix"
-
-    override val baseUrl = "https://megaflix.ac"
-
-    override val lang = "pt-BR"
-
-    override val supportsLatest = true
-
-    override fun headersBuilder() = super.headersBuilder().add("Referer", "$baseUrl/")
-
-    private val preferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    // ============================== Popular ===============================
-    override fun popularAnimeRequest(page: Int) = GET(baseUrl)
-
-    override fun popularAnimeSelector() = "section#widget_list_movies_series-5 li > article"
-
-    override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
-        title = element.selectFirst("h2.entry-title")!!.text()
-        setUrlWithoutDomain(element.selectFirst("a.lnk-blk")!!.attr("href"))
-        thumbnail_url = element.selectFirst("img")?.absUrl("src")
-    }
-
-    override fun popularAnimeNextPageSelector() = null
-
-    // =============================== Latest ===============================
-    override fun latestUpdatesRequest(page: Int): Request {
-        val pageType = preferences.getString(PREF_LATEST_PAGE_KEY, PREF_LATEST_PAGE_DEFAULT)!!
-        return GET("$baseUrl/$pageType/page/$page")
-    }
-
-    override fun latestUpdatesSelector() = "li > article"
-
-    override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
-
-    override fun latestUpdatesNextPageSelector() = "div.nav-links > a:containsOwn(PRÓXIMO)"
-
-    // =============================== Search ===============================
-    override suspend fun getSearchAnime(
-        page: Int,
-        query: String,
-        filters: AnimeFilterList,
-    ): AnimesPage {
-        return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
-            val path = query.removePrefix(PREFIX_SEARCH)
-            client.newCall(GET("$baseUrl/$path"))
-                .awaitSuccess()
-                .use(::searchAnimeByPathParse)
-        } else {
-            super.getSearchAnime(page, query, filters)
-        }
-    }
-
-    private fun searchAnimeByPathParse(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 {
-        return if (query.isNotBlank()) {
-            GET("$baseUrl/page/$page/?s=$query")
-        } else {
-            val genre = MegaflixFilters.getGenre(filters)
-            GET("$baseUrl/categoria/$genre/page/$page")
-        }
-    }
-
-    override fun searchAnimeSelector() = latestUpdatesSelector()
-
-    override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
-
-    override fun searchAnimeNextPageSelector() = latestUpdatesNextPageSelector()
-
-    override fun getFilterList() = MegaflixFilters.FILTER_LIST
-
-    // =========================== Anime Details ============================
-    override fun animeDetailsParse(document: Document) = SAnime.create().apply {
-        setUrlWithoutDomain(document.location())
-        val infos = document.selectFirst("div.bd > article.post.single")!!
-        title = infos.selectFirst("h1.entry-title")!!.text()
-        thumbnail_url = infos.selectFirst("img")?.absUrl("src")
-        genre = infos.select("span.genres > a").eachText().joinToString()
-        description = infos.selectFirst("div.description")?.text()
-    }
-
-    // ============================== Episodes ==============================
-    override fun episodeListSelector() = "li > article.episodes"
-    private fun seasonListSelector() = "section.episodes div.choose-season > a"
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val doc = response.asJsoup()
-        val seasons = doc.select(seasonListSelector())
-        return when {
-            seasons.isEmpty() -> listOf(
-                SEpisode.create().apply {
-                    name = "Filme"
-                    setUrlWithoutDomain(doc.location())
-                    episode_number = 1F
-                },
-            )
-
-            else -> seasons.parallelFlatMapBlocking(::episodesFromSeason).reversed()
-        }
-    }
-
-    private suspend fun episodesFromSeason(seasonElement: Element): List<SEpisode> {
-        return seasonElement.attr("href").let { url ->
-            client.newCall(GET(url, headers)).await()
-                .asJsoup()
-                .select(episodeListSelector())
-                .map(::episodeFromElement)
-        }
-    }
-
-    override fun episodeFromElement(element: Element) = SEpisode.create().apply {
-        name = element.selectFirst("h2.entry-title")!!.text()
-        setUrlWithoutDomain(element.selectFirst("a.lnk-blk")!!.attr("href"))
-        episode_number = element.selectFirst("span.num-epi")?.run {
-            text().split("x").last().toFloatOrNull() ?: 0F
-        } ?: 0F
-    }
-
-    // ============================ Video Links =============================
-    override fun videoListParse(response: Response): List<Video> {
-        val items = response.asJsoup().select(videoListSelector())
-        return items
-            .parallelCatchingFlatMapBlocking { element ->
-                val language = element.text().substringAfter("-")
-                val id = element.attr("href")
-                val url = element.closest("body")?.selectFirst("div$id iframe")
-                    ?.let {
-                        val iframeUrl = it.attr("src")
-                        client.newCall(GET(iframeUrl, headers))
-                            .execute()
-                            .asJsoup()
-                            .selectFirst("iframe")
-                            ?.attr("src")
-                    } ?: return@parallelCatchingFlatMapBlocking emptyList()
-
-                getVideoList(url, language)
-            }
-    }
-
-    /*--------------------------------Video extractors------------------------------------*/
-    private val webViewResolver by lazy { WebViewResolver() }
-    private val playlistUtils by lazy { PlaylistUtils(client, headers) }
-
-    private val mixdropExtractor by lazy { MixDropExtractor(client) }
-    private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
-    private val megaflixExtractor by lazy { MegaflixExtractor(client, headers) }
-    private val voeExtractor by lazy { VoeExtractor(client) }
-    private val filemoonExtractor by lazy { FilemoonExtractor(client) }
-    private val vidHideExtractor by lazy { VidHideExtractor(client, headers) }
-    private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
-
-    private fun getVideoList(url: String, language: String): List<Video> {
-        return when {
-            arrayOf("mixdrop", "mixdroop", "mix").any(url) -> mixdropExtractor.videoFromUrl(url, language)
-            arrayOf("streamtape", "stp", "stape").any(url) -> streamtapeExtractor.videosFromUrl(url, "StreamTape - $language")
-            arrayOf("mflix").any(url) -> megaflixExtractor.videosFromUrl(url, language)
-            arrayOf("voe").any(url) -> voeExtractor.videosFromUrl(url, "$language ")
-            arrayOf("filemoon", "moonplayer").any(url) -> filemoonExtractor.videosFromUrl(url, prefix = "$language Filemoon:")
-            arrayOf("vidhide", "vid.").any(url) -> vidHideExtractor.videosFromUrl(url, videoNameGen = { "$language VidHide:$it" })
-            arrayOf("wishembed", "streamwish", "strwish", "wish", "jwplayerhls").any(url) -> streamWishExtractor.videosFromUrl(url, videoNameGen = { "$language StreamWish:$it" })
-            arrayOf("fembedder").any(url) -> {
-                val webViewResult = webViewResolver.getUrl(url, headers)
-                if (webViewResult.isBlank()) {
-                    return emptyList()
-                }
-                return if (webViewResult.contains("m3u8")) {
-                    playlistUtils.extractFromHls(webViewResult)
-                } else {
-                    listOf(Video(url, "Default", url))
-                }
-            }
-            else -> emptyList()
-        }
-    }
-
-    override fun videoListSelector() = "aside.video-options li a"
-
-    override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
-
-    override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
-
-    // ============================== Settings ==============================
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        ListPreference(screen.context).apply {
-            key = PREF_QUALITY_KEY
-            title = PREF_QUALITY_TITLE
-            entries = PREF_QUALITY_ENTRIES
-            entryValues = PREF_QUALITY_ENTRIES
-            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_LANGUAGE_KEY
-            title = PREF_LANGUAGE_TITLE
-            entries = PREF_LANGUAGE_VALUES
-            entryValues = PREF_LANGUAGE_VALUES
-            setDefaultValue(PREF_LANGUAGE_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_LATEST_PAGE_KEY
-            title = PREF_LATEST_PAGE_TITLE
-            entries = PREF_LATEST_PAGE_ENTRIES
-            entryValues = PREF_LATEST_PAGE_VALUES
-            setDefaultValue(PREF_LATEST_PAGE_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)
-    }
-
-    // ============================= Utilities ==============================
-    override fun List<Video>.sort(): List<Video> {
-        val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
-        val lang = preferences.getString(PREF_LANGUAGE_KEY, PREF_LANGUAGE_DEFAULT)!!
-        return sortedWith(
-            compareBy(
-                { it.quality.contains(quality) },
-                { it.quality.contains(lang) },
-            ),
-        ).reversed()
-    }
-
-    private fun Array<String>.any(url: String): Boolean = this.any { url.contains(it, ignoreCase = true) }
-
-    companion object {
-        const val PREFIX_SEARCH = "path:"
-
-        private const val PREF_QUALITY_KEY = "preferred_quality"
-        private const val PREF_QUALITY_TITLE = "Qualidade preferida"
-        private const val PREF_QUALITY_DEFAULT = "720p"
-        private val PREF_QUALITY_ENTRIES = arrayOf("360p", "480p", "720p", "1080p")
-
-        private const val PREF_LANGUAGE_KEY = "pref_language"
-        private const val PREF_LANGUAGE_DEFAULT = "Legendado"
-        private const val PREF_LANGUAGE_TITLE = "Língua/tipo preferido"
-        private val PREF_LANGUAGE_VALUES = arrayOf("Legendado", "Dublado")
-
-        private const val PREF_LATEST_PAGE_KEY = "pref_latest_page"
-        private const val PREF_LATEST_PAGE_DEFAULT = "series"
-        private const val PREF_LATEST_PAGE_TITLE = "Página de últimos adicionados"
-        private val PREF_LATEST_PAGE_ENTRIES = arrayOf(
-            "Filmes",
-            "Séries",
-        )
-        private val PREF_LATEST_PAGE_VALUES = arrayOf(
-            "filmes",
-            "series",
-        )
-    }
-}
diff --git a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/MegaflixFilters.kt b/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/MegaflixFilters.kt
deleted file mode 100644
index 07f6de38..00000000
--- a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/MegaflixFilters.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.megaflix
-
-import eu.kanade.tachiyomi.animesource.model.AnimeFilter
-import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
-
-object MegaflixFilters {
-
-    open class QueryPartFilter(
-        displayName: String,
-        val vals: Array<Pair<String, String>>,
-    ) : AnimeFilter.Select<String>(
-        displayName,
-        vals.map { it.first }.toTypedArray(),
-    ) {
-        fun toQueryPart() = vals[state].second
-    }
-
-    private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
-        return (first { it is R } as QueryPartFilter).toQueryPart()
-    }
-
-    class GenreFilter : QueryPartFilter("Gênero", MegaflixFiltersData.GENRES)
-
-    val FILTER_LIST get() = AnimeFilterList(
-        AnimeFilter.Header(MegaflixFiltersData.IGNORE_SEARCH_MSG),
-        GenreFilter(),
-    )
-
-    fun getGenre(filters: AnimeFilterList) = filters.asQueryPart<GenreFilter>()
-
-    private object MegaflixFiltersData {
-        const val IGNORE_SEARCH_MSG = "NOTA: O filtro é IGNORADO ao usar a pesquisa."
-
-        val GENRES = arrayOf(
-            Pair("Animação", "animacao"),
-            Pair("Aventura", "aventura"),
-            Pair("Ação", "acao"),
-            Pair("Biografia", "biografia"),
-            Pair("Comédia", "comedia"),
-            Pair("Crime", "crime"),
-            Pair("Documentário", "documentario"),
-            Pair("Drama", "drama"),
-            Pair("Esporte", "esporte"),
-            Pair("Família", "familia"),
-            Pair("Fantasia", "fantasia"),
-            Pair("Faroeste", "faroeste"),
-            Pair("Ficção científica", "ficcao-cientifica"),
-            Pair("Guerra", "guerra"),
-            Pair("História", "historia"),
-            Pair("Mistério", "misterio"),
-            Pair("Musical", "musical"),
-            Pair("Música", "musica"),
-            Pair("Romance", "romance"),
-            Pair("Show", "show"),
-            Pair("Terror", "terror"),
-            Pair("Thriller", "thriller"),
-        )
-    }
-}
diff --git a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/MegaflixUrlActivity.kt b/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/MegaflixUrlActivity.kt
deleted file mode 100644
index cec9a93d..00000000
--- a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/MegaflixUrlActivity.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.megaflix
-
-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://megaflix.ac/<type>/<item> intents
- * and redirects them to the main Aniyomi process.
- */
-class MegaflixUrlActivity : 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 path = "${pathSegments[0]}/${pathSegments[1]}"
-            val mainIntent = Intent().apply {
-                action = "eu.kanade.tachiyomi.ANIMESEARCH"
-                putExtra("query", "${Megaflix.PREFIX_SEARCH}$path")
-                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)
-    }
-}
diff --git a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/WebViewResolver.kt b/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/WebViewResolver.kt
deleted file mode 100644
index a1c377b0..00000000
--- a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/WebViewResolver.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.megaflix
-
-import android.annotation.SuppressLint
-import android.app.Application
-import android.os.Handler
-import android.os.Looper
-import android.webkit.WebResourceRequest
-import android.webkit.WebResourceResponse
-import android.webkit.WebView
-import android.webkit.WebViewClient
-import okhttp3.Headers
-import uy.kohesive.injekt.injectLazy
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-
-class WebViewResolver() {
-    private val context: Application by injectLazy()
-    private val handler by lazy { Handler(Looper.getMainLooper()) }
-
-    @SuppressLint("SetJavaScriptEnabled")
-    fun getUrl(origRequestUrl: String, origRequestheader: Headers): String {
-        val latch = CountDownLatch(1)
-        var webView: WebView? = null
-        var resultUrl = ""
-        val headers = origRequestheader.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()
-
-        handler.post {
-            val webview = WebView(context)
-            webView = webview
-            with(webview.settings) {
-                javaScriptEnabled = true
-                domStorageEnabled = true
-                databaseEnabled = true
-                useWideViewPort = false
-                loadWithOverviewMode = false
-                userAgentString = origRequestheader["User-Agent"]
-            }
-            webview.webViewClient = object : WebViewClient() {
-                override fun shouldInterceptRequest(
-                    view: WebView,
-                    request: WebResourceRequest,
-                ): WebResourceResponse? {
-                    val url = request.url.toString()
-                    if (VIDEO_REGEX.containsMatchIn(url)) {
-                        resultUrl = url
-                        latch.countDown()
-                    }
-                    return super.shouldInterceptRequest(view, request)
-                }
-            }
-
-            webView?.loadUrl(origRequestUrl, headers)
-        }
-
-        latch.await(TIMEOUT_SEC, TimeUnit.SECONDS)
-
-        handler.post {
-            webView?.stopLoading()
-            webView?.destroy()
-            webView = null
-        }
-        return resultUrl
-    }
-
-    companion object {
-        const val TIMEOUT_SEC: Long = 20
-        private val VIDEO_REGEX by lazy { Regex("\\.(mp4|m3u8)") }
-    }
-}
diff --git a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/extractors/MegaflixExtractor.kt b/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/extractors/MegaflixExtractor.kt
deleted file mode 100644
index 849511d8..00000000
--- a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/extractors/MegaflixExtractor.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.megaflix.extractors
-
-import dev.datlag.jsunpacker.JsUnpacker
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
-import eu.kanade.tachiyomi.network.GET
-import okhttp3.Headers
-import okhttp3.OkHttpClient
-
-class MegaflixExtractor(private val client: OkHttpClient, private val headers: Headers) {
-    private val playlistUtils by lazy { PlaylistUtils(client, headers) }
-
-    fun videosFromUrl(url: String, lang: String = ""): List<Video> {
-        val unpacked = client.newCall(GET(url, headers)).execute()
-            .body.string()
-            .let(JsUnpacker::unpackAndCombine)
-            ?.replace("\\", "")
-            ?: return emptyList()
-
-        val playlistUrl = unpacked.substringAfter("file':'").substringBefore("'")
-
-        return playlistUtils.extractFromHls(
-            playlistUrl,
-            "https://megaflix.ac",
-            videoNameGen = { "Megaflix($lang) - $it" },
-        )
-    }
-}
diff --git a/src/pt/pifansubs/res/web_hi_res_512.png b/src/pt/pifansubs/res/web_hi_res_512.png
deleted file mode 100644
index e586e291..00000000
Binary files a/src/pt/pifansubs/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/pt/pobreflix/build.gradle b/src/pt/pobreflix/build.gradle
deleted file mode 100644
index a3dcec69..00000000
--- a/src/pt/pobreflix/build.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-ext {
-    extName = 'Pobreflix'
-    extClass = '.Pobreflix'
-    themePkg = 'dooplay'
-    baseUrl = 'https://pobreflix.global'
-    overrideVersionCode = 18
-    isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(":lib:filemoon-extractor"))
-    implementation(project(':lib:fireplayer-extractor'))
-    implementation(project(":lib:streamwish-extractor"))
-    implementation(project(":lib:streamtape-extractor"))
-    implementation(project(":lib:playlist-utils"))
-    implementation(libs.jsunpacker)
-}
diff --git a/src/pt/pobreflix/res/mipmap-hdpi/ic_launcher.png b/src/pt/pobreflix/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index d5f7231a..00000000
Binary files a/src/pt/pobreflix/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/pobreflix/res/mipmap-mdpi/ic_launcher.png b/src/pt/pobreflix/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 208cbaa0..00000000
Binary files a/src/pt/pobreflix/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/pobreflix/res/mipmap-xhdpi/ic_launcher.png b/src/pt/pobreflix/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 30444a76..00000000
Binary files a/src/pt/pobreflix/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/pobreflix/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/pobreflix/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index cbd9fe5d..00000000
Binary files a/src/pt/pobreflix/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/pobreflix/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/pobreflix/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 35b2f3c2..00000000
Binary files a/src/pt/pobreflix/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/Pobreflix.kt b/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/Pobreflix.kt
deleted file mode 100644
index c1159018..00000000
--- a/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/Pobreflix.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.pobreflix
-
-import android.util.Base64
-import eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors.MyStreamExtractor
-import eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors.PlayerFlixExtractor
-import eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors.SuperFlixExtractor
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
-import eu.kanade.tachiyomi.lib.fireplayerextractor.FireplayerExtractor
-import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
-import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
-import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.Response
-
-class Pobreflix : DooPlay(
-    "pt-BR",
-    "Pobreflix",
-    "https://pobreflix.global",
-) {
-    // ============================== Popular ===============================
-    override fun popularAnimeSelector() = "div.featured div.poster"
-
-    // =============================== Latest ===============================
-    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/series/page/$page/", headers)
-
-    // ============================ Video Links =============================
-    private val fireplayerExtractor by lazy { FireplayerExtractor(client) }
-    private val filemoonExtractor by lazy { FilemoonExtractor(client) }
-    private val mystreamExtractor by lazy { MyStreamExtractor(client, headers) }
-    private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
-    private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
-    private val playerflixExtractor by lazy { PlayerFlixExtractor(client, headers, ::genericExtractor) }
-    private val superflixExtractor by lazy { SuperFlixExtractor(client, headers, ::genericExtractor) }
-    private val supercdnExtractor by lazy { SuperFlixExtractor(client, headers, ::genericExtractor, "https://supercdn.org") }
-
-    override fun videoListParse(response: Response): List<Video> {
-        val doc = response.asJsoup()
-        return doc.select("div.source-box > a").parallelCatchingFlatMapBlocking {
-            val data = it.attr("href").trim().toHttpUrl().queryParameter("auth")
-                ?.let { Base64.decode(it, Base64.DEFAULT) }
-                ?.let(::String)
-                ?: return@parallelCatchingFlatMapBlocking emptyList()
-            val url = data.replace("\\", "").substringAfter("url\":\"").substringBefore('"')
-            genericExtractor(url)
-        }
-    }
-
-    private fun genericExtractor(url: String, language: String = ""): List<Video> {
-        val langSubstr = if (language.isBlank()) "" else "[$language] "
-        return when {
-            url.contains("superflix") ->
-                superflixExtractor.videosFromUrl(url)
-            url.contains("supercdn") ->
-                supercdnExtractor.videosFromUrl(url)
-            url.contains("filemoon") ->
-                filemoonExtractor.videosFromUrl(url, "${langSubstr}Filemoon - ", headers = headers)
-            url.contains("watch.brplayer") || url.contains("/watch?v=") ->
-                mystreamExtractor.videosFromUrl(url, language)
-            url.contains("brbeast") ->
-                fireplayerExtractor.videosFromUrl(url = url, videoNameGen = { "${langSubstr}BrBeast - $it" })
-            url.contains("embedplayer") ->
-                fireplayerExtractor.videosFromUrl(url = url, videoNameGen = { "${langSubstr}EmbedPlayer - $it" })
-            url.contains("superembeds") ->
-                fireplayerExtractor.videosFromUrl(url = url, videoNameGen = { "${langSubstr}SuperEmbeds - $it" })
-            url.contains("streamtape") ->
-                streamtapeExtractor.videosFromUrl(url, "${langSubstr}Streamtape")
-            url.contains("filelions") ->
-                streamwishExtractor.videosFromUrl(url, videoNameGen = { "${langSubstr}FileLions - $it" })
-            url.contains("streamwish") ->
-                streamwishExtractor.videosFromUrl(url, videoNameGen = { "${langSubstr}Streamwish - $it" })
-            url.contains("playerflix") ->
-                playerflixExtractor.videosFromUrl(url)
-            else -> emptyList()
-        }
-    }
-}
diff --git a/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/extractors/MyStreamExtractor.kt b/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/extractors/MyStreamExtractor.kt
deleted file mode 100644
index b510b9db..00000000
--- a/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/extractors/MyStreamExtractor.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors
-
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
-import eu.kanade.tachiyomi.network.GET
-import okhttp3.Headers
-import okhttp3.OkHttpClient
-
-// From animeworldindia
-class MyStreamExtractor(private val client: OkHttpClient, private val headers: Headers) {
-
-    private val playlistUtils by lazy { PlaylistUtils(client, headers) }
-
-    fun videosFromUrl(url: String, language: String): List<Video> {
-        val host = url.substringBefore("/watch?")
-
-        val response = client.newCall(GET(url, headers)).execute()
-        val body = response.body.string()
-
-        val codePart = body
-            .substringAfter("sniff(") // Video function
-            .substringBefore(",[")
-
-        val streamCode = codePart
-            .substringAfterLast(",\"") // our beloved hash
-            .substringBefore('"')
-
-        val id = codePart.substringAfter(",\"").substringBefore('"') // required ID
-
-        val streamUrl = "$host/m3u8/$id/$streamCode/master.txt?s=1&cache=1"
-
-        val cookie = response.headers.firstOrNull {
-            it.first.startsWith("set-cookie", true) && it.second.startsWith("PHPSESSID", true)
-        }?.second?.substringBefore(";") ?: ""
-
-        val newHeaders = headers.newBuilder()
-            .set("cookie", cookie)
-            .set("accept", "*/*")
-            .build()
-
-        return playlistUtils.extractFromHls(
-            streamUrl,
-            masterHeaders = newHeaders,
-            videoHeaders = newHeaders,
-            videoNameGen = { "[$language] MyStream: $it" },
-        )
-    }
-}
diff --git a/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/extractors/PlayerFlixExtractor.kt b/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/extractors/PlayerFlixExtractor.kt
deleted file mode 100644
index 0dab9340..00000000
--- a/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/extractors/PlayerFlixExtractor.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors
-
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-import okhttp3.Headers
-import okhttp3.OkHttpClient
-
-class PlayerFlixExtractor(
-    private val client: OkHttpClient,
-    private val defaultHeaders: Headers,
-    private val genericExtractor: (String, String) -> List<Video>,
-) {
-    fun videosFromUrl(url: String): List<Video> {
-        val doc = client.newCall(GET(url, defaultHeaders)).execute().asJsoup()
-
-        val items = doc.select("#hostList div.buttonLoadHost").mapNotNull {
-            val url = it.attr("onclick")
-                .substringAfter('"', "")
-                .substringBefore('"')
-                ?: return@mapNotNull null
-
-            val language = if (it.hasClass("hostDub")) {
-                "Dublado"
-            } else {
-                "Legendado"
-            }
-
-            language to url // (Language, videoId)
-        }
-
-        return items.parallelCatchingFlatMapBlocking { genericExtractor(it.second, it.first) }
-    }
-}
diff --git a/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/extractors/SuperFlixExtractor.kt b/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/extractors/SuperFlixExtractor.kt
deleted file mode 100644
index 92a2dcbe..00000000
--- a/src/pt/pobreflix/src/eu/kanade/tachiyomi/animeextension/pt/pobreflix/extractors/SuperFlixExtractor.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors
-
-import eu.kanade.tachiyomi.animesource.model.Video
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.network.await
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-import okhttp3.FormBody
-import okhttp3.Headers
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.OkHttpClient
-import uy.kohesive.injekt.injectLazy
-
-class SuperFlixExtractor(
-    private val client: OkHttpClient,
-    private val defaultHeaders: Headers,
-    private val genericExtractor: (String, String) -> List<Video>,
-    private val host: String = "https://superflixapi.dev",
-) {
-    private val json: Json by injectLazy()
-
-    fun videosFromUrl(url: String): List<Video> {
-        val links = linksFromUrl(url)
-
-        val fixedLinks = links.parallelCatchingFlatMapBlocking {
-            val (language, linkUrl) = it
-            when {
-                linkUrl.contains("?vid=") -> linksFromPlayer(linkUrl, language)
-                else -> listOf(it)
-            }
-        }
-
-        return fixedLinks.parallelCatchingFlatMapBlocking { genericExtractor(it.second, it.first) }
-    }
-
-    private suspend fun linksFromPlayer(url: String, language: String): List<Pair<String, String>> {
-        val httpUrl = url.toHttpUrl()
-        val id = httpUrl.queryParameter("vid")!!
-        val headers = defaultHeaders.newBuilder()
-            .set("referer", "$host/")
-            .set("origin", host)
-            .build()
-
-        val doc = client.newCall(GET(url, headers)).await().asJsoup()
-
-        val baseUrl = "https://" + httpUrl.host
-        val apiUrl = "$baseUrl/ajax_sources.php"
-
-        val apiHeaders = headers.newBuilder()
-            .set("referer", url)
-            .set("origin", baseUrl)
-            .set("X-Requested-With", "XMLHttpRequest")
-            .build()
-
-        return doc.select("ul > li[data-order-value]").mapNotNull {
-            val name = it.attr("data-dropdown-value")
-            val order = it.attr("data-order-value")
-
-            val formBody = FormBody.Builder()
-                .add("vid", id)
-                .add("alternative", name)
-                .add("ord", order)
-                .build()
-
-            val req = client.newCall(POST(apiUrl, apiHeaders, formBody)).await()
-                .body.string()
-
-            runCatching {
-                val iframeUrl = json.decodeFromString<PlayerLinkDto>(req).iframe!!
-                val iframeServer = iframeUrl.toHttpUrl().queryParameter("sv")!!
-                language to when (name) {
-                    "1xbet" -> "https://watch.brplayer.site/watch?v=${iframeServer.trim('/')}"
-                    else -> iframeServer
-                }
-            }.getOrNull()
-        }
-    }
-
-    @Serializable
-    data class PlayerLinkDto(val iframe: String? = null)
-
-    private fun linksFromUrl(url: String): List<Pair<String, String>> {
-        val doc = client.newCall(GET(url, defaultHeaders)).execute().asJsoup()
-
-        val items = doc.select("div.select_languages").mapNotNull {
-            val id = it.nextElementSibling()
-                ?.selectFirst("div[data-id]")
-                ?.attr("data-id")
-                ?: return@mapNotNull null
-
-            val language = it.text()
-                .replace("Disponível", "")
-                .replace("disponível", "")
-                .replace("Apenas", "")
-                .replace("em", "")
-                .trim()
-
-            language to id // (Language, videoId)
-        }
-
-        val headers = defaultHeaders.newBuilder()
-            .set("Origin", host)
-            .set("Referer", url)
-            .set("X-Requested-With", "XMLHttpRequest")
-            .build()
-
-        return items.mapNotNull {
-            runCatching {
-                it.first to getLink(it.second, headers)!!
-            }.getOrNull()
-        }
-    }
-
-    private fun getLink(id: String, headers: Headers): String? {
-        val body = FormBody.Builder()
-            .add("action", "getPlayer")
-            .add("video_id", id)
-            .build()
-
-        val res = client.newCall(POST("$host/api", headers, body)).execute()
-            .body.string()
-
-        return json.decodeFromString<ApiResponseDto>(res).data?.video_url
-    }
-
-    @Serializable
-    data class ApiResponseDto(val data: DataDto? = null)
-
-    @Serializable
-    data class DataDto(val video_url: String? = null)
-}
diff --git a/src/pt/vizer/AndroidManifest.xml b/src/pt/vizer/AndroidManifest.xml
deleted file mode 100644
index b9524492..00000000
--- a/src/pt/vizer/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <application>
-        <activity
-            android:name=".pt.vizer.VizerUrlActivity"
-            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="vizer.tv"
-                    android:pathPattern="/..*/..*/..*"
-                    android:scheme="https" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/src/pt/vizer/build.gradle b/src/pt/vizer/build.gradle
deleted file mode 100644
index f5ed1181..00000000
--- a/src/pt/vizer/build.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
-ext {
-    extName = 'Vizer.tv'
-    extClass = '.Vizer'
-    extVersionCode = 24
-    isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
-
-dependencies {
-    implementation(project(':lib:fireplayer-extractor'))
-    implementation(project(':lib:mixdrop-extractor'))
-    implementation(project(':lib:playlist-utils'))
-    implementation(project(':lib:streamtape-extractor'))
-    implementation(libs.jsunpacker)
-}
diff --git a/src/pt/vizer/res/mipmap-hdpi/ic_launcher.png b/src/pt/vizer/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index ae34c91d..00000000
Binary files a/src/pt/vizer/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/vizer/res/mipmap-mdpi/ic_launcher.png b/src/pt/vizer/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index b78a281b..00000000
Binary files a/src/pt/vizer/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/vizer/res/mipmap-xhdpi/ic_launcher.png b/src/pt/vizer/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index e07ab4a7..00000000
Binary files a/src/pt/vizer/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/vizer/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/vizer/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index a5654df6..00000000
Binary files a/src/pt/vizer/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/vizer/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/vizer/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b904558c..00000000
Binary files a/src/pt/vizer/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/Vizer.kt b/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/Vizer.kt
deleted file mode 100644
index caa95dbb..00000000
--- a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/Vizer.kt
+++ /dev/null
@@ -1,359 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.vizer
-
-import android.app.Application
-import androidx.preference.ListPreference
-import androidx.preference.PreferenceScreen
-import eu.kanade.tachiyomi.animeextension.pt.vizer.VizerFilters.FilterSearchParams
-import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.EpisodeListDto
-import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.HostersDto
-import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.SearchItemDto
-import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.SearchResultDto
-import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.VideoDto
-import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.VideoListDto
-import eu.kanade.tachiyomi.animeextension.pt.vizer.interceptor.WebViewResolver
-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.fireplayerextractor.FireplayerExtractor
-import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
-import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.network.awaitSuccess
-import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
-import eu.kanade.tachiyomi.util.asJsoup
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMap
-import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
-import eu.kanade.tachiyomi.util.parseAs
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.MediaType.Companion.toMediaType
-import okhttp3.Request
-import okhttp3.RequestBody.Companion.toRequestBody
-import okhttp3.Response
-import org.jsoup.nodes.Element
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import kotlin.time.Duration.Companion.seconds
-
-class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
-
-    override val name = "Vizer.tv"
-
-    override val baseUrl = "https://novizer.com"
-    private val apiUrl = "$baseUrl/includes/ajax"
-
-    override val lang = "pt-BR"
-
-    override val supportsLatest = true
-
-    override fun headersBuilder() = super.headersBuilder().add("Referer", "$baseUrl/")
-
-    private val episodesClient by lazy {
-        client.newBuilder().rateLimitHost(baseUrl.toHttpUrl(), 1, 1.5.seconds).build()
-    }
-
-    private val preferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    private val webViewResolver by lazy { WebViewResolver(headers) }
-
-    // ============================== Popular ===============================
-    override fun popularAnimeRequest(page: Int): Request {
-        val pageType = preferences.getString(PREF_POPULAR_PAGE_KEY, PREF_POPULAR_PAGE_DEFAULT)!!
-        val params = FilterSearchParams(
-            orderBy = "vzViews",
-            orderWay = "desc",
-            type = pageType,
-        )
-        return searchAnimeRequest(page, "", params)
-    }
-
-    override fun popularAnimeParse(response: Response): AnimesPage {
-        val result = response.parseAs<SearchResultDto>()
-        val animes = result.items.values.map(::animeFromObject)
-        val hasNext = result.quantity == 35
-        return AnimesPage(animes, hasNext)
-    }
-
-    private fun animeFromObject(item: SearchItemDto) = SAnime.create().apply {
-        val (urlslug, imgslug) = when {
-            item.status.isBlank() -> Pair("filme", "movies")
-            else -> Pair("serie", "series")
-        }
-        url = "/$urlslug/online/${item.url}"
-        title = item.title
-        status = when (item.status) {
-            "Retornando" -> SAnime.ONGOING
-            else -> SAnime.COMPLETED
-        }
-        thumbnail_url = "$baseUrl/content/$imgslug/posterPt/342/${item.id}.webp"
-    }
-
-    // =============================== Latest ===============================
-    override fun latestUpdatesRequest(page: Int) = apiRequest("getHomeSliderSeries=1")
-
-    override fun latestUpdatesParse(response: Response): AnimesPage {
-        val parsedData = response.parseAs<SearchResultDto>()
-        val animes = parsedData.items.values.map(::animeFromObject)
-        return AnimesPage(animes, false)
-    }
-
-    // =============================== Search ===============================
-    override fun getFilterList(): AnimeFilterList = VizerFilters.FILTER_LIST
-
-    override fun searchAnimeParse(response: Response) = popularAnimeParse(response)
-
-    override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
-        return if (query.startsWith(PREFIX_SEARCH)) {
-            val path = query.removePrefix(PREFIX_SEARCH).replace("/", "/online/")
-            client.newCall(GET("$baseUrl/$path"))
-                .awaitSuccess()
-                .use(::searchAnimeByPathParse)
-        } else {
-            super.getSearchAnime(page, query, filters)
-        }
-    }
-
-    private fun searchAnimeByPathParse(response: Response): AnimesPage {
-        val details = animeDetailsParse(response).apply {
-            setUrlWithoutDomain(response.request.url.toString())
-            initialized = true
-        }
-
-        return AnimesPage(listOf(details), false)
-    }
-
-    override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
-        val params = VizerFilters.getSearchParameters(filters)
-        return searchAnimeRequest(page, query, params)
-    }
-
-    private fun searchAnimeRequest(page: Int, query: String, params: FilterSearchParams): Request {
-        val urlBuilder = "$apiUrl/ajaxPagination.php".toHttpUrl().newBuilder()
-            .addQueryParameter("page", "${page - 1}")
-            .addQueryParameter("categoryFilterYearMin", params.minYear)
-            .addQueryParameter("categoryFilterYearMax", params.maxYear)
-            .addQueryParameter("categoryFilterOrderBy", params.orderBy)
-            .addQueryParameter("categoryFilterOrderWay", params.orderWay)
-            .apply {
-                if (query.isNotBlank()) addQueryParameter("search", query)
-
-                when (params.type) {
-                    "Movies" -> {
-                        addQueryParameter("saga", "0")
-                        addQueryParameter("categoriesListMovies", params.genre)
-                    }
-                    else -> {
-                        addQueryParameter("categoriesListSeries", params.genre)
-                        val isAnime = params.type == "anime"
-                        addQueryParameter("anime", if (isAnime) "1" else "0")
-                    }
-                }
-            }
-        return GET(urlBuilder.build(), headers)
-    }
-
-    // =========================== Anime Details ============================
-    override fun animeDetailsParse(response: Response) = SAnime.create().apply {
-        val doc = response.asJsoup()
-        setUrlWithoutDomain(doc.location())
-        title = doc.selectFirst("section.ai > h2")!!.text()
-        thumbnail_url = doc.selectFirst("meta[property=og:image]")!!.attr("content")
-
-        description = buildString {
-            append(doc.selectFirst("span.desc")!!.text() + "\n")
-            doc.selectFirst("div.year")?.also { append("\nAno: ", it.text()) }
-            doc.selectFirst("div.tm")?.also { append("\nDuração: ", it.text()) }
-            doc.selectFirst("a.rating")?.also { append("\nNota: ", it.text()) }
-        }
-    }
-
-    // ============================== Episodes ==============================
-    private fun getSeasonEps(seasonElement: Element): List<SEpisode> {
-        val id = seasonElement.attr("data-season-id")
-        val sname = seasonElement.text()
-        val response = episodesClient.newCall(apiRequest("getEpisodes=$id")).execute()
-        val episodes = response.parseAs<EpisodeListDto>().episodes
-            .values
-            .filter { it.released === true }
-            .map {
-                SEpisode.create().apply {
-                    name = "$sname: Ep ${it.name}".run {
-                        if (!it.title.contains("Episode ")) {
-                            this + " - ${it.title}"
-                        } else {
-                            this
-                        }
-                    }
-                    episode_number = it.name.toFloatOrNull() ?: 0F
-                    url = it.id
-                }
-            }
-        return episodes
-    }
-
-    override fun episodeListParse(response: Response): List<SEpisode> {
-        val doc = response.asJsoup()
-        val seasons = doc.select("div.seasons div.list div.item[data-season-id]")
-        return if (seasons.size > 0) {
-            seasons.flatMap(::getSeasonEps).reversed()
-        } else {
-            listOf(
-                SEpisode.create().apply {
-                    name = "Filme"
-                    episode_number = 1F
-                    url = response.request.url.toString()
-                },
-            )
-        }
-    }
-
-    // ============================ Video Links =============================
-    override fun videoListRequest(episode: SEpisode): Request {
-        val url = episode.url
-        return if (url.startsWith("https")) {
-            // Its an real url, maybe from a movie
-            GET(url, headers)
-        } else {
-            // Fake url, its an ID that will be used to get episode languages
-            // (sub/dub) and then return the video link
-            apiRequest("getEpisodeData=$url")
-        }
-    }
-
-    override fun videoListParse(response: Response): List<Video> {
-        val body = response.body.string()
-        val videoObjectList = if (body.startsWith("{")) {
-            body.parseAs<VideoListDto>().videos.values.toList()
-        } else {
-            val doc = response.asJsoup(body)
-            doc.select("div.audios div[data-load-player]").mapNotNull {
-                try {
-                    val movieHosters = it.attr("data-players").parseAs<HostersDto>()
-                    val movieId = it.attr("data-load-player")
-                    val movieLang = if (it.hasClass("legendado")) "1" else "0"
-                    VideoDto(movieId, movieLang).apply { hosters = movieHosters }
-                } catch (_: Throwable) { null }
-            }
-        }
-
-        return videoObjectList.parallelCatchingFlatMapBlocking(::getVideosFromObject)
-    }
-
-    private val mixdropExtractor by lazy { MixDropExtractor(client) }
-    private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
-    private val fireplayerExtractor by lazy { FireplayerExtractor(client) }
-
-    private suspend fun getVideosFromObject(videoObj: VideoDto): List<Video> {
-        val hosters = videoObj.hosters ?: return emptyList()
-
-        val langPrefix = if (videoObj.lang == "1") "LEG" else "DUB"
-
-        return hosters.iterator().parallelCatchingFlatMap { (name, status) ->
-            // Always try the warezcdn
-            if (status != 3 && name != "warezcdn") return@parallelCatchingFlatMap emptyList()
-            val url = getPlayerUrl(videoObj.id, name)
-            if (url.isNullOrBlank()) {
-                return@parallelCatchingFlatMap emptyList()
-            }
-            when (name) {
-                "mixdrop" -> mixdropExtractor.videosFromUrl(url, langPrefix)
-                "streamtape" -> streamtapeExtractor.videosFromUrl(url, "StreamTape($langPrefix)")
-                "warezcdn" -> fireplayerExtractor.videosFromUrl(url, videoNameGen = { "WarezCDN($langPrefix) - $it" })
-                else -> emptyList()
-            }
-        }
-    }
-
-    // ============================== Settings ==============================
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        ListPreference(screen.context).apply {
-            key = PREF_POPULAR_PAGE_KEY
-            title = PREF_POPULAR_PAGE_TITLE
-            entries = PREF_POPULAR_PAGE_ENTRIES
-            entryValues = PREF_POPULAR_PAGE_VALUES
-            setDefaultValue(PREF_POPULAR_PAGE_DEFAULT)
-            summary = "%s"
-        }.also(screen::addPreference)
-
-        ListPreference(screen.context).apply {
-            key = PREF_PLAYER_KEY
-            title = PREF_PLAYER_TITLE
-            entries = PREF_PLAYER_ARRAY
-            entryValues = PREF_PLAYER_ARRAY
-            setDefaultValue(PREF_PLAYER_DEFAULT)
-            summary = "%s"
-        }.also(screen::addPreference)
-
-        ListPreference(screen.context).apply {
-            key = PREF_LANGUAGE_KEY
-            title = PREF_LANGUAGE_TITLE
-            entries = PREF_LANGUAGE_ENTRIES
-            entryValues = PREF_LANGUAGE_VALUES
-            setDefaultValue(PREF_LANGUAGE_DEFAULT)
-            summary = "%s"
-        }.also(screen::addPreference)
-    }
-
-    // ============================= Utilities ==============================
-    private val noRedirectClient = client.newBuilder().followRedirects(false).build()
-
-    private fun getPlayerUrl(id: String, name: String): String? {
-        return webViewResolver.getUrl("$baseUrl/embed/getEmbed.php?id=$id&sv=$name", "$baseUrl/termos")
-    }
-
-    private fun apiRequest(body: String): Request {
-        val reqBody = body.toRequestBody("application/x-www-form-urlencoded".toMediaType())
-        val newHeaders = headersBuilder().add("x-requested-with", "XMLHttpRequest").build()
-        return POST("$apiUrl/publicFunctions.php", newHeaders, body = reqBody)
-    }
-
-    override fun List<Video>.sort(): List<Video> {
-        val player = preferences.getString(PREF_PLAYER_KEY, PREF_PLAYER_DEFAULT)!!
-        val language = preferences.getString(PREF_LANGUAGE_KEY, PREF_LANGUAGE_DEFAULT)!!
-        return sortedWith(
-            compareBy(
-                { it.quality.contains(player) },
-                { it.quality.contains(language) },
-            ),
-        ).reversed()
-    }
-
-    companion object {
-        private const val PREF_POPULAR_PAGE_KEY = "pref_popular_page"
-        private const val PREF_POPULAR_PAGE_DEFAULT = "movie"
-        private const val PREF_POPULAR_PAGE_TITLE = "Página de Populares"
-        private val PREF_POPULAR_PAGE_ENTRIES = arrayOf(
-            "Animes",
-            "Filmes",
-            "Séries",
-        )
-        private val PREF_POPULAR_PAGE_VALUES = arrayOf(
-            "anime",
-            "movie",
-            "serie",
-        )
-
-        private const val PREF_PLAYER_KEY = "pref_player"
-        private const val PREF_PLAYER_DEFAULT = "MixDrop"
-        private const val PREF_PLAYER_TITLE = "Player/Server favorito"
-        private val PREF_PLAYER_ARRAY = arrayOf(
-            "MixDrop",
-            "StreamTape",
-            "WarezCDN",
-        )
-
-        private const val PREF_LANGUAGE_KEY = "pref_language"
-        private const val PREF_LANGUAGE_DEFAULT = "LEG"
-        private const val PREF_LANGUAGE_TITLE = "Língua/tipo preferido"
-        private val PREF_LANGUAGE_ENTRIES = arrayOf("Legendado", "Dublado")
-        private val PREF_LANGUAGE_VALUES = arrayOf("LEG", "DUB")
-
-        const val PREFIX_SEARCH = "path:"
-    }
-}
diff --git a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/VizerFilters.kt b/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/VizerFilters.kt
deleted file mode 100644
index 3940da20..00000000
--- a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/VizerFilters.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.vizer
-
-import eu.kanade.tachiyomi.animeextension.pt.vizer.VizerFilters.VizerFiltersData.CURRENT_YEAR
-import eu.kanade.tachiyomi.animesource.model.AnimeFilter
-import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
-import java.util.Calendar
-
-object VizerFilters {
-    open class QueryPartFilter(
-        displayName: String,
-        val vals: Array<Pair<String, String>>,
-    ) : AnimeFilter.Select<String>(
-        displayName,
-        vals.map { it.first }.toTypedArray(),
-    ) {
-
-        fun toQueryPart() = vals[state].second
-    }
-
-    private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
-        return (first { it is R } as QueryPartFilter).toQueryPart()
-    }
-
-    class TypeFilter : QueryPartFilter("Tipo", VizerFiltersData.TYPES)
-    class MinYearFilter : QueryPartFilter("Ano (min)", VizerFiltersData.MIN_YEARS)
-    class MaxYearFilter : QueryPartFilter("Ano (max)", VizerFiltersData.MAX_YEARS)
-    class GenreFilter : QueryPartFilter("Categoria", VizerFiltersData.GENRES)
-
-    class SortFilter : AnimeFilter.Sort(
-        "Ordernar por",
-        VizerFiltersData.ORDERS.map { it.first }.toTypedArray(),
-        Selection(0, false),
-    )
-
-    val FILTER_LIST get() = AnimeFilterList(
-        TypeFilter(),
-        MinYearFilter(),
-        MaxYearFilter(),
-        GenreFilter(),
-        SortFilter(),
-    )
-
-    data class FilterSearchParams(
-        val type: String = "anime",
-        val minYear: String = "1890",
-        val maxYear: String = CURRENT_YEAR.toString(),
-        val genre: String = "all",
-        val orderBy: String = "rating",
-        val orderWay: String = "desc",
-    )
-
-    internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
-        if (filters.isEmpty()) return FilterSearchParams()
-
-        val sortFilter = filters.firstOrNull { it is SortFilter } as? SortFilter
-        val (orderBy, ascending) = sortFilter?.state?.run {
-            val order = VizerFiltersData.ORDERS[index].second
-            val orderWay = if (ascending) "asc" else "desc"
-
-            Pair(order, orderWay)
-        } ?: Pair("rating", "desc")
-
-        return FilterSearchParams(
-            filters.asQueryPart<TypeFilter>(),
-            filters.asQueryPart<MinYearFilter>(),
-            filters.asQueryPart<MaxYearFilter>(),
-            filters.asQueryPart<GenreFilter>(),
-            orderBy,
-            ascending,
-        )
-    }
-
-    private object VizerFiltersData {
-        val TYPES = arrayOf(
-            Pair("Animes", "anime"),
-            Pair("Filmes", "Movies"),
-            Pair("Series", "Series"),
-        )
-        val CURRENT_YEAR by lazy {
-            Calendar.getInstance()[Calendar.YEAR]
-        }
-        val MAX_YEARS = (CURRENT_YEAR downTo 1890).map {
-            Pair(it.toString(), it.toString())
-        }.toTypedArray()
-
-        val MIN_YEARS = MAX_YEARS.reversed().toTypedArray()
-
-        val ORDERS = arrayOf(
-            Pair("Popularidade", "vzViews"),
-            Pair("Ano", "year"),
-            Pair("Título", "title"),
-            Pair("Rating", "rating"),
-        )
-
-        val GENRES = arrayOf(
-            Pair("Todas", "all"),
-            Pair("Animação", "animacao"),
-            Pair("Aventura", "aventura"),
-            Pair("Ação", "acao"),
-            Pair("Comédia", "comedia"),
-            Pair("Crime", "crime"),
-            Pair("Documentário", "documentario"),
-            Pair("Drama", "drama"),
-            Pair("Família", "familia"),
-            Pair("Fantasia", "fantasia"),
-            Pair("Faroeste", "faroeste"),
-            Pair("Guerra", "guerra"),
-            Pair("LGBTQ+", "lgbt"),
-            Pair("Mistério", "misterio"),
-            Pair("Músical", "musical"),
-            Pair("Romance", "romance"),
-            Pair("Suspense", "suspense"),
-            Pair("Terror", "terror"),
-        )
-    }
-}
diff --git a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/VizerUrlActivity.kt b/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/VizerUrlActivity.kt
deleted file mode 100644
index be22e4b7..00000000
--- a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/VizerUrlActivity.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.vizer
-
-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://vizer.tv/[anime|filme|serie]/online/<slug> intents
- * and redirects them to the main Aniyomi process.
- */
-class VizerUrlActivity : Activity() {
-
-    private val tag = "VizerUrlActivity"
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        val pathSegments = intent?.data?.pathSegments
-        if (pathSegments != null && pathSegments.size > 1) {
-            val query = "${pathSegments[0]}/${pathSegments[2]}"
-            val searchQuery = Vizer.PREFIX_SEARCH + query
-            val mainIntent = Intent().apply {
-                action = "eu.kanade.tachiyomi.ANIMESEARCH"
-                putExtra("query", searchQuery)
-                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)
-    }
-}
diff --git a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/dto/VizerDto.kt b/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/dto/VizerDto.kt
deleted file mode 100644
index f8bf3c96..00000000
--- a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/dto/VizerDto.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.vizer.dto
-
-import eu.kanade.tachiyomi.util.parseAs
-import kotlinx.serialization.EncodeDefault
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.builtins.serializer
-import kotlinx.serialization.json.JsonElement
-import kotlinx.serialization.json.JsonPrimitive
-import kotlinx.serialization.json.JsonTransformingSerializer
-import kotlinx.serialization.json.booleanOrNull
-import kotlinx.serialization.json.jsonPrimitive
-
-typealias FakeList<T> = Map<String, T>
-
-@Serializable
-class SearchResultDto(
-    val quantity: Int = 0,
-    @EncodeDefault
-    @SerialName("list")
-    val items: FakeList<SearchItemDto> = emptyMap(),
-)
-
-@Serializable
-class SearchItemDto(
-    val id: String,
-    val title: String,
-    val url: String,
-    @EncodeDefault
-    val status: String = "",
-)
-
-@Serializable
-class EpisodeListDto(
-    @SerialName("list")
-    val episodes: FakeList<EpisodeItemDto>,
-)
-
-@Serializable
-class EpisodeItemDto(
-    val id: String,
-    val name: String,
-    @Serializable(with = BooleanSerializer::class)
-    val released: Boolean,
-    val title: String,
-)
-
-@Serializable
-class VideoListDto(
-    @SerialName("list")
-    val videos: FakeList<VideoDto>,
-)
-
-@Serializable
-class VideoDto(
-    val id: String,
-    val lang: String,
-    @SerialName("players")
-    private val players: String? = null,
-) {
-    var hosters = try {
-        players?.parseAs<HostersDto>()
-    } catch (e: Throwable) {
-        null
-    }
-}
-
-@Serializable
-class HostersDto(
-    val mixdrop: Int = 0,
-    val streamtape: Int = 0,
-    val warezcdn: Int = 0,
-) {
-    operator fun iterator(): List<Pair<String, Int>> {
-        return listOf(
-            "mixdrop" to mixdrop,
-            "streamtape" to streamtape,
-            "warezcdn" to warezcdn,
-        )
-    }
-}
-
-object BooleanSerializer : JsonTransformingSerializer<Boolean>(Boolean.serializer()) {
-    override fun transformDeserialize(element: JsonElement): JsonElement {
-        require(element is JsonPrimitive)
-        return if (element.jsonPrimitive.isString && element.jsonPrimitive.content == "true") {
-            JsonPrimitive(true)
-        } else {
-            JsonPrimitive(element.jsonPrimitive.booleanOrNull ?: false)
-        }
-    }
-}
diff --git a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/interceptor/WebViewResolver.kt b/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/interceptor/WebViewResolver.kt
deleted file mode 100644
index 177f2704..00000000
--- a/src/pt/vizer/src/eu/kanade/tachiyomi/animeextension/pt/vizer/interceptor/WebViewResolver.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.pt.vizer.interceptor
-
-import android.annotation.SuppressLint
-import android.app.Application
-import android.os.Handler
-import android.os.Looper
-import android.util.Log
-import android.webkit.WebResourceRequest
-import android.webkit.WebResourceResponse
-import android.webkit.WebView
-import android.webkit.WebViewClient
-import okhttp3.Headers
-import uy.kohesive.injekt.injectLazy
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-
-class WebViewResolver(private val globalHeaders: Headers) {
-    private val context: Application by injectLazy()
-    private val handler by lazy { Handler(Looper.getMainLooper()) }
-    private val tag by lazy { javaClass.simpleName }
-
-    @SuppressLint("SetJavaScriptEnabled")
-    fun getUrl(origRequestUrl: String, baseUrl: String): String? {
-        val latch = CountDownLatch(1)
-        var webView: WebView? = null
-        var result: String? = null
-
-        handler.post {
-            val webview = WebView(context)
-            webView = webview
-            with(webview.settings) {
-                javaScriptEnabled = true
-                domStorageEnabled = true
-                databaseEnabled = true
-                useWideViewPort = false
-                loadWithOverviewMode = false
-                userAgentString = globalHeaders["User-Agent"]
-            }
-            webview.webViewClient = object : WebViewClient() {
-                override fun shouldInterceptRequest(
-                    view: WebView,
-                    request: WebResourceRequest,
-                ): WebResourceResponse? {
-                    val url = request.url.toString()
-                    Log.d(tag, "Checking url $url")
-                    if (VIDEO_REGEX.containsMatchIn(url)) {
-                        result = url
-                        latch.countDown()
-                    }
-                    return super.shouldInterceptRequest(view, request)
-                }
-
-                override fun onPageFinished(view: WebView?, url: String?) {
-                    Log.d(tag, "onPageFinished $url")
-                    super.onPageFinished(view, url)
-
-                    view?.evaluateJavascript("document.body.innerHTML += '<iframe src=\"" + origRequestUrl + "\" scrolling=\"no\" frameborder=\"0\" allowfullscreen=\"\" webkitallowfullscreen=\"\" mozallowfullscreen=\"\"></iframe>'") {}
-                }
-            }
-
-            webView?.loadUrl(baseUrl)
-        }
-
-        latch.await(TIMEOUT_SEC, TimeUnit.SECONDS)
-
-        handler.post {
-            webView?.stopLoading()
-            webView?.destroy()
-            webView = null
-        }
-        return result
-    }
-
-    companion object {
-        const val TIMEOUT_SEC: Long = 25
-        private val VIDEO_REGEX by lazy { Regex("//(mixdrop|streamtape|warezcdn)|/video/") }
-    }
-}
diff --git a/src/tr/turkanime/res/web_hi_res_512.png b/src/tr/turkanime/res/web_hi_res_512.png
deleted file mode 100644
index 4f080baa..00000000
Binary files a/src/tr/turkanime/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/tr/turkanime/src/eu/kanade/tachiyomi/animeextension/tr/turkanime/TurkAnime.kt b/src/tr/turkanime/src/eu/kanade/tachiyomi/animeextension/tr/turkanime/TurkAnime.kt
index b86c84e3..22028b3f 100644
--- a/src/tr/turkanime/src/eu/kanade/tachiyomi/animeextension/tr/turkanime/TurkAnime.kt
+++ b/src/tr/turkanime/src/eu/kanade/tachiyomi/animeextension/tr/turkanime/TurkAnime.kt
@@ -37,6 +37,7 @@ import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.POST
 import eu.kanade.tachiyomi.network.await
+import eu.kanade.tachiyomi.network.awaitSuccess
 import eu.kanade.tachiyomi.util.asJsoup
 import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
 import eu.kanade.tachiyomi.util.parallelMapBlocking
@@ -106,13 +107,70 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
 
     override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector()
 
+    // ============================== Filters ===============================
+    override fun getFilterList(): AnimeFilterList = TurkAnimeFilters.FILTER_LIST
+
     // =============================== Search ===============================
     override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) =
-        POST(
-            "$baseUrl/arama?sayfa=$page",
-            headers,
-            FormBody.Builder().add("arama", query).build(),
-        )
+        throw UnsupportedOperationException()
+
+    private fun searchAnimeRequest(
+        page: Int,
+        query: String,
+        filters: TurkAnimeFilters.FilterSearchParams,
+    ): Request {
+        return if (query.isBlank()) {
+            POST(
+                "$baseUrl/ajax/animeler?sayfa=$page",
+                xmlHeader,
+                FormBody.Builder().apply {
+                    filters.type.takeLast(3).forEach {
+                        add("tip[]", it)
+                    }
+                    filters.genre.takeLast(3).forEach {
+                        add("tur[]", it)
+                    }
+                    filters.year.takeLast(2).forEach {
+                        add("yil[]", it)
+                    }
+                    filters.point.takeLast(2).forEach {
+                        add("puan[]", it)
+                    }
+                    filters.like.takeLast(2).forEach {
+                        add("begeni[]", it)
+                    }
+                    filters.producer.takeLast(3).forEach {
+                        add("yapimci[]", it)
+                    }
+                    filters.studio.takeLast(3).forEach {
+                        add("studyo[]", it)
+                    }
+                    add("listele", filters.list)
+                    add("sezon", filters.season)
+                    add("sirala", filters.sort)
+                }.build(),
+            )
+        } else {
+            POST(
+                "$baseUrl/arama?sayfa=$page",
+                headers,
+                FormBody.Builder().add("arama", query).build(),
+            )
+        }
+    }
+
+    override suspend fun getSearchAnime(
+        page: Int,
+        query: String,
+        filters: AnimeFilterList,
+    ): AnimesPage {
+        val params = TurkAnimeFilters.getSearchParameters(filters)
+        return client.newCall(searchAnimeRequest(page, query, params))
+            .awaitSuccess()
+            .let { response ->
+                searchAnimeParse(response)
+            }
+    }
 
     override fun searchAnimeParse(response: Response): AnimesPage {
         val document = response.asJsoup()
diff --git a/src/tr/turkanime/src/eu/kanade/tachiyomi/animeextension/tr/turkanime/TurkAnimeFilters.kt b/src/tr/turkanime/src/eu/kanade/tachiyomi/animeextension/tr/turkanime/TurkAnimeFilters.kt
new file mode 100644
index 00000000..47583d25
--- /dev/null
+++ b/src/tr/turkanime/src/eu/kanade/tachiyomi/animeextension/tr/turkanime/TurkAnimeFilters.kt
@@ -0,0 +1,1357 @@
+package eu.kanade.tachiyomi.animeextension.tr.turkanime
+
+import eu.kanade.tachiyomi.animesource.model.AnimeFilter
+import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
+
+object TurkAnimeFilters {
+    open class QueryPartFilter(
+        displayName: String,
+        private val vals: Array<Pair<String, String>>,
+    ) : AnimeFilter.Select<String>(
+        displayName,
+        vals.map { it.second }.toTypedArray(),
+    ) {
+        fun toQueryPart() = vals[state].first
+    }
+
+    private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
+        return (getFirst<R>() as QueryPartFilter).toQueryPart()
+    }
+
+    private inline fun <reified R> AnimeFilterList.getFirst(): R {
+        return first { it is R } as R
+    }
+
+    private inline fun <reified R> AnimeFilterList.parseCheckbox(
+        options: Array<Pair<String, String>>,
+    ): List<String> {
+        return (getFirst<R>() as CheckBoxFilterList).state
+            .filter { it.state }
+            .map { checkbox -> options.find { it.second == checkbox.name }!!.first }
+            .filter(String::isNotBlank)
+    }
+
+    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)
+
+    class TypeFilter : CheckBoxFilterList(
+        "Kategori",
+        TurkAnimeFiltersData.TYPE.map { CheckBoxVal(it.second, false) },
+    )
+
+    class GenreFilter : CheckBoxFilterList(
+        "Tür",
+        TurkAnimeFiltersData.GENRE.map { CheckBoxVal(it.second, false) },
+    )
+
+    class YearFilter : CheckBoxFilterList(
+        "Başlama & Bitiş Yılı",
+        TurkAnimeFiltersData.YEAR.map { CheckBoxVal(it.second, false) },
+    )
+
+    class PointFilter : CheckBoxFilterList(
+        "Başlama & Bitiş Puanı",
+        TurkAnimeFiltersData.POINT.map { CheckBoxVal(it.second, false) },
+    )
+
+    class LikeFilter : CheckBoxFilterList(
+        "Başlama & Bitiş Beğenisi",
+        TurkAnimeFiltersData.LIKE.map { CheckBoxVal(it.second, false) },
+    )
+
+    class ProducerFilter : CheckBoxFilterList(
+        "Yapımcı Firma",
+        TurkAnimeFiltersData.PRODUCER.map { CheckBoxVal(it.second, false) },
+    )
+
+    class StudioFilter : CheckBoxFilterList(
+        "Stüdyo",
+        TurkAnimeFiltersData.PRODUCER.map { CheckBoxVal(it.second, false) },
+    )
+
+    class ListFilter : QueryPartFilter("Listeleme", TurkAnimeFiltersData.LIST)
+    class SeasonFilter : QueryPartFilter("Sezon", TurkAnimeFiltersData.SEASON)
+    class SortFilter : QueryPartFilter("Sıralama", TurkAnimeFiltersData.SORT)
+
+    private fun Int.headerWarning(): String = "--- Seçilen son $this seçenek kullanılır ---"
+
+    val FILTER_LIST
+        get() = AnimeFilterList(
+            AnimeFilter.Header(3.headerWarning()),
+            TypeFilter(),
+            AnimeFilter.Separator(),
+            AnimeFilter.Header(3.headerWarning()),
+            GenreFilter(),
+            AnimeFilter.Separator(),
+            AnimeFilter.Header(2.headerWarning()),
+            YearFilter(),
+            AnimeFilter.Separator(),
+            AnimeFilter.Header(2.headerWarning()),
+            PointFilter(),
+            AnimeFilter.Separator(),
+            AnimeFilter.Header(2.headerWarning()),
+            LikeFilter(),
+            AnimeFilter.Separator(),
+            AnimeFilter.Header("Listenin yüklemesi zaman alabilir"),
+            AnimeFilter.Header(3.headerWarning()),
+            ProducerFilter(),
+            AnimeFilter.Separator(),
+            AnimeFilter.Header("Listenin yüklemesi zaman alabilir"),
+            AnimeFilter.Header(3.headerWarning()),
+            StudioFilter(),
+            AnimeFilter.Separator(),
+            ListFilter(),
+            SeasonFilter(),
+            SortFilter(),
+        )
+
+    data class FilterSearchParams(
+        val type: List<String> = emptyList(),
+        val genre: List<String> = emptyList(),
+        val year: List<String> = emptyList(),
+        val point: List<String> = emptyList(),
+        val like: List<String> = emptyList(),
+        val producer: List<String> = emptyList(),
+        val studio: List<String> = emptyList(),
+        val list: String = "",
+        val season: String = "",
+        val sort: String = "",
+    )
+
+    internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
+        if (filters.isEmpty()) return FilterSearchParams()
+
+        return FilterSearchParams(
+            filters.parseCheckbox<TypeFilter>(TurkAnimeFiltersData.TYPE),
+            filters.parseCheckbox<GenreFilter>(TurkAnimeFiltersData.GENRE),
+            filters.parseCheckbox<YearFilter>(TurkAnimeFiltersData.YEAR),
+            filters.parseCheckbox<PointFilter>(TurkAnimeFiltersData.POINT),
+            filters.parseCheckbox<LikeFilter>(TurkAnimeFiltersData.LIKE),
+            filters.parseCheckbox<ProducerFilter>(TurkAnimeFiltersData.PRODUCER),
+            filters.parseCheckbox<StudioFilter>(TurkAnimeFiltersData.PRODUCER),
+            filters.asQueryPart<ListFilter>(),
+            filters.asQueryPart<SeasonFilter>(),
+            filters.asQueryPart<SortFilter>(),
+        )
+    }
+
+    private object TurkAnimeFiltersData {
+        val GENRE = arrayOf(
+            Pair("1", "Aksiyon"),
+            Pair("3", "Arabalar"),
+            Pair("38", "Askeri"),
+            Pair("5", "Avangard"),
+            Pair("24", "Bilim Kurgu"),
+            Pair("16", "Büyü"),
+            Pair("15", "Çocuklar"),
+            Pair("37", "Doğaüstü Güçler"),
+            Pair("17", "Dövüş Sanatları"),
+            Pair("8", "Dram"),
+            Pair("9", "Ecchi"),
+            Pair("10", "Fantastik"),
+            Pair("41", "Gerilim"),
+            Pair("7", "Gizem"),
+            Pair("35", "Harem"),
+            Pair("43", "Josei"),
+            Pair("4", "Komedi"),
+            Pair("14", "Korku"),
+            Pair("2", "Macera"),
+            Pair("18", "Mecha"),
+            Pair("19", "Müzik"),
+            Pair("23", "Okul"),
+            Pair("11", "Oyun"),
+            Pair("20", "Parodi"),
+            Pair("39", "Polisiye"),
+            Pair("40", "Psikolojik"),
+            Pair("22", "Romantizm"),
+            Pair("21", "Samuray"),
+            Pair("42", "Seinen"),
+            Pair("6", "Şeytanlar"),
+            Pair("25", "Shoujo"),
+            Pair("26", "Shoujo Ai"),
+            Pair("27", "Shounen"),
+            Pair("28", "Shounen Ai"),
+            Pair("30", "Spor"),
+            Pair("31", "Süper Güçler"),
+            Pair("13", "Tarihi"),
+            Pair("29", "Uzay"),
+            Pair("32", "Vampir"),
+            Pair("33", "Yaoi"),
+            Pair("36", "Yaşamdan Kesitler"),
+            Pair("34", "Yuri"),
+        )
+
+        val YEAR = arrayOf(
+            Pair("1930", "1930"),
+            Pair("1931", "1931"),
+            Pair("1932", "1932"),
+            Pair("1933", "1933"),
+            Pair("1934", "1934"),
+            Pair("1935", "1935"),
+            Pair("1936", "1936"),
+            Pair("1937", "1937"),
+            Pair("1938", "1938"),
+            Pair("1939", "1939"),
+            Pair("1940", "1940"),
+            Pair("1941", "1941"),
+            Pair("1942", "1942"),
+            Pair("1943", "1943"),
+            Pair("1944", "1944"),
+            Pair("1945", "1945"),
+            Pair("1946", "1946"),
+            Pair("1947", "1947"),
+            Pair("1948", "1948"),
+            Pair("1949", "1949"),
+            Pair("1950", "1950"),
+            Pair("1951", "1951"),
+            Pair("1952", "1952"),
+            Pair("1953", "1953"),
+            Pair("1954", "1954"),
+            Pair("1955", "1955"),
+            Pair("1956", "1956"),
+            Pair("1957", "1957"),
+            Pair("1958", "1958"),
+            Pair("1959", "1959"),
+            Pair("1960", "1960"),
+            Pair("1961", "1961"),
+            Pair("1962", "1962"),
+            Pair("1963", "1963"),
+            Pair("1964", "1964"),
+            Pair("1965", "1965"),
+            Pair("1966", "1966"),
+            Pair("1967", "1967"),
+            Pair("1968", "1968"),
+            Pair("1969", "1969"),
+            Pair("1970", "1970"),
+            Pair("1971", "1971"),
+            Pair("1972", "1972"),
+            Pair("1973", "1973"),
+            Pair("1974", "1974"),
+            Pair("1975", "1975"),
+            Pair("1976", "1976"),
+            Pair("1977", "1977"),
+            Pair("1978", "1978"),
+            Pair("1979", "1979"),
+            Pair("1980", "1980"),
+            Pair("1981", "1981"),
+            Pair("1982", "1982"),
+            Pair("1983", "1983"),
+            Pair("1984", "1984"),
+            Pair("1985", "1985"),
+            Pair("1986", "1986"),
+            Pair("1987", "1987"),
+            Pair("1988", "1988"),
+            Pair("1989", "1989"),
+            Pair("1990", "1990"),
+            Pair("1991", "1991"),
+            Pair("1992", "1992"),
+            Pair("1993", "1993"),
+            Pair("1994", "1994"),
+            Pair("1995", "1995"),
+            Pair("1996", "1996"),
+            Pair("1997", "1997"),
+            Pair("1998", "1998"),
+            Pair("1999", "1999"),
+            Pair("2000", "2000"),
+            Pair("2001", "2001"),
+            Pair("2002", "2002"),
+            Pair("2003", "2003"),
+            Pair("2004", "2004"),
+            Pair("2005", "2005"),
+            Pair("2006", "2006"),
+            Pair("2007", "2007"),
+            Pair("2008", "2008"),
+            Pair("2009", "2009"),
+            Pair("2010", "2010"),
+            Pair("2011", "2011"),
+            Pair("2012", "2012"),
+            Pair("2013", "2013"),
+            Pair("2014", "2014"),
+            Pair("2015", "2015"),
+            Pair("2016", "2016"),
+            Pair("2017", "2017"),
+            Pair("2018", "2018"),
+            Pair("2019", "2019"),
+            Pair("2020", "2020"),
+            Pair("2021", "2021"),
+            Pair("2022", "2022"),
+            Pair("2023", "2023"),
+            Pair("2024", "2024"),
+            Pair("2025", "2025"),
+            Pair("2026", "2026"),
+        ).reversedArray()
+
+        val TYPE = arrayOf(
+            Pair("TV", "TV"),
+            Pair("Movie", "Movie"),
+            Pair("OVA", "OVA"),
+            Pair("ONA", "ONA"),
+            Pair("Special", "Special"),
+        )
+
+        val LIST = arrayOf(
+            Pair("", "Seçiniz"),
+            Pair("0", "Alfabetik"),
+            Pair("1", "Puan"),
+            Pair("2", "Beğeni"),
+            Pair("3", "Tarih"),
+            Pair("4", "MAL Puanı"),
+        )
+
+        val SEASON = arrayOf(
+            Pair("", "Seçiniz"),
+            Pair("1", "İlkbahar"),
+            Pair("2", "Yaz"),
+            Pair("3", "Sonbahar"),
+            Pair("4", "Kış"),
+        )
+
+        val SORT = arrayOf(
+            Pair("", "Seçiniz"),
+            Pair("ASC", "A > Z"),
+            Pair("DESC", "Z > A"),
+        )
+
+        val POINT = arrayOf(
+            Pair("1", "1"),
+            Pair("2", "2"),
+            Pair("3", "3"),
+            Pair("4", "4"),
+            Pair("5", "5"),
+            Pair("6", "6"),
+            Pair("7", "7"),
+            Pair("8", "8"),
+            Pair("9", "9"),
+            Pair("10", "10"),
+        )
+
+        val LIKE = arrayOf(
+            Pair("1000", "1000"),
+            Pair("2000", "2000"),
+            Pair("3000", "3000"),
+            Pair("4000", "4000"),
+            Pair("5000", "5000"),
+            Pair("6000", "6000"),
+            Pair("7000", "7000"),
+            Pair("8000", "8000"),
+            Pair("9000", "9000"),
+            Pair("10000", "10000"),
+            Pair("11000", "11000"),
+            Pair("12000", "12000"),
+            Pair("13000", "13000"),
+            Pair("14000", "14000"),
+            Pair("15000", "15000"),
+            Pair("16000", "16000"),
+            Pair("17000", "17000"),
+            Pair("18000", "18000"),
+            Pair("19000", "19000"),
+            Pair("20000", "20000"),
+        )
+
+        val PRODUCER = arrayOf(
+            Pair("1", "Studio Pierrot"),
+            Pair("2", "Kyoto Animation"),
+            Pair("3", "Gonzo"),
+            Pair("4", "Bones"),
+            Pair("5", "Bee Train"),
+            Pair("6", "Gainax"),
+            Pair("7", "J.C.Staff"),
+            Pair("8", "Artland"),
+            Pair("10", "Production I.G"),
+            Pair("11", "Madhouse"),
+            Pair("13", "Studio 4°C"),
+            Pair("14", "Sunrise"),
+            Pair("15", "Sony Pictures Entertainment"),
+            Pair("16", "TV Tokyo"),
+            Pair("17", "Aniplex"),
+            Pair("18", "Toei Animation"),
+            Pair("21", "Studio Ghibli"),
+            Pair("22", "Nippon Animation"),
+            Pair("23", "Bandai Visual"),
+            Pair("24", "Studio Fantasia"),
+            Pair("25", "Milky Animation Label"),
+            Pair("27", "Xebec"),
+            Pair("28", "OLM"),
+            Pair("29", "VAP"),
+            Pair("30", "Ajia-Do"),
+            Pair("31", "Geneon Universal Entertainment"),
+            Pair("32", "Manglobe"),
+            Pair("33", "WOWOW"),
+            Pair("34", "Hal Film Maker"),
+            Pair("35", "Seven Arcs"),
+            Pair("36", "Studio Gallop"),
+            Pair("37", "Studio Deen"),
+            Pair("38", "Arms"),
+            Pair("39", "Daume"),
+            Pair("40", "m.o.e."),
+            Pair("41", "Satelight"),
+            Pair("42", "GDH"),
+            Pair("43", "ufotable"),
+            Pair("44", "Shaft"),
+            Pair("45", "Pink Pineapple"),
+            Pair("47", "Khara"),
+            Pair("48", "AIC"),
+            Pair("49", "Remic"),
+            Pair("50", "KSS"),
+            Pair("51", "Diomedea"),
+            Pair("52", "Avex Entertainment"),
+            Pair("53", "Dentsu"),
+            Pair("54", "Mook Animation"),
+            Pair("55", "TV Asahi"),
+            Pair("56", "A-1 Pictures"),
+            Pair("58", "Square Enix"),
+            Pair("59", "Beat Frog"),
+            Pair("60", "Actas"),
+            Pair("61", "Frontier Works"),
+            Pair("62", "Shogakukan Productions"),
+            Pair("63", "Trinet Entertainment"),
+            Pair("64", "Sotsu"),
+            Pair("65", "Tokyo Movie Shinsha"),
+            Pair("67", "OB Planning"),
+            Pair("68", "Mushi Production"),
+            Pair("69", "Front Line"),
+            Pair("70", "Nomad"),
+            Pair("71", "Mellow Head"),
+            Pair("72", "Artmic"),
+            Pair("73", "TMS Entertainment"),
+            Pair("75", "Imagin"),
+            Pair("76", "Yomiuri Telecasting Corporation"),
+            Pair("77", "APPP"),
+            Pair("78", "Picture Magic"),
+            Pair("79", "Genco"),
+            Pair("80", "Trans Arts"),
+            Pair("81", "Radix"),
+            Pair("82", "Marvelous Entertainment"),
+            Pair("83", "AIC Spirits"),
+            Pair("84", "Studio Rikka"),
+            Pair("85", "Konami"),
+            Pair("86", "Group TAC"),
+            Pair("88", "AIC A.S.T.A."),
+            Pair("89", "BeSTACK"),
+            Pair("91", "feel."),
+            Pair("92", "Starchild Records"),
+            Pair("93", "Studio Flag"),
+            Pair("94", "Telecom Animation Film"),
+            Pair("95", "Doga Kobo"),
+            Pair("96", "Yumeta Company"),
+            Pair("97", "ADV Films"),
+            Pair("98", "Sav! The World Productions"),
+            Pair("99", "Kitty Films"),
+            Pair("100", "TV Osaka"),
+            Pair("101", "Studio Hibari"),
+            Pair("102", "FUNimation Entertainment"),
+            Pair("103", "Tatsunoko Production"),
+            Pair("104", "Lantis"),
+            Pair("107", "Plum"),
+            Pair("108", "Media Factory"),
+            Pair("109", "Shochiku"),
+            Pair("110", "Triangle Staff"),
+            Pair("111", "NHK"),
+            Pair("113", "Kadokawa Shoten"),
+            Pair("114", "Studio Matrix"),
+            Pair("116", "Broccoli"),
+            Pair("117", "Studio Izena"),
+            Pair("118", "SynergySP"),
+            Pair("119", "Viz Media"),
+            Pair("120", "TNK"),
+            Pair("121", "Active"),
+            Pair("122", "Tokyo Kids"),
+            Pair("123", "Victor Entertainment"),
+            Pair("124", "Animac"),
+            Pair("126", "Studio Comet"),
+            Pair("127", "Yomiko Advertising"),
+            Pair("129", "Silky’s"),
+            Pair("130", "CherryLips"),
+            Pair("131", "G&G Entertainment"),
+            Pair("132", "P.A. Works"),
+            Pair("133", "Shinyusha"),
+            Pair("134", "HoriPro"),
+            Pair("135", "MediaNet"),
+            Pair("136", "Vega Entertainment"),
+            Pair("137", "PPM"),
+            Pair("138", "Himajin Planning"),
+            Pair("139", "Nihon Ad Systems"),
+            Pair("140", "Animax"),
+            Pair("141", "Toei Video"),
+            Pair("142", "Asatsu DK"),
+            Pair("143", "Mainichi Broadcasting System"),
+            Pair("144", "Pony Canyon"),
+            Pair("145", "TBS"),
+            Pair("146", "CBC"),
+            Pair("147", "SKY Perfect Well Think"),
+            Pair("148", "Hakusensha"),
+            Pair("149", "SME Visual Works"),
+            Pair("150", "Sanrio"),
+            Pair("152", "Green Bunny"),
+            Pair("153", "SoftX"),
+            Pair("154", "E&G Films"),
+            Pair("155", "Animate Film"),
+            Pair("156", "Polygram Japan"),
+            Pair("157", "BMG Japan"),
+            Pair("158", "Kids Station"),
+            Pair("159", "Kodansha"),
+            Pair("160", "Rondo Robe"),
+            Pair("161", "Sogo Vision"),
+            Pair("163", "Asread"),
+            Pair("164", "d-rights"),
+            Pair("165", "Marubeni"),
+            Pair("166", "Movic"),
+            Pair("167", "Sega"),
+            Pair("168", "Shelty"),
+            Pair("169", "Fuji TV"),
+            Pair("170", "Imagica"),
+            Pair("171", "A Line"),
+            Pair("172", "Alchemist"),
+            Pair("175", "Planet"),
+            Pair("176", "Ginga Ya"),
+            Pair("177", "Studio Kyuuma"),
+            Pair("178", "Trilogy Future Studio"),
+            Pair("179", "A.C.G.T."),
+            Pair("182", "Age"),
+            Pair("183", "Agent 21"),
+            Pair("184", "Ai ga areba Daijobu"),
+            Pair("185", "Aiko"),
+            Pair("189", "GEN Productions"),
+            Pair("190", "RCC Chugoku Broadcasting"),
+            Pair("191", "Eiken"),
+            Pair("192", "Shinkuukan"),
+            Pair("193", "Idea Factory"),
+            Pair("194", "ANIK"),
+            Pair("195", "Pixy"),
+            Pair("196", "Production Reed"),
+            Pair("198", "Gakken"),
+            Pair("199", "Studio Nue"),
+            Pair("200", "Tezuka Productions"),
+            Pair("204", "Pioneer LDC"),
+            Pair("206", "Digital Works"),
+            Pair("207", "Magic Bus"),
+            Pair("208", "Three Fat Samurai"),
+            Pair("209", "Sol Blade"),
+            Pair("210", "Studio Tulip"),
+            Pair("211", "Rakuonsha"),
+            Pair("212", "Venus Vangard"),
+            Pair("213", "Half H.P Studio"),
+            Pair("214", "BS-i"),
+            Pair("215", "Nelvana"),
+            Pair("216", "Studio Tron"),
+            Pair("217", "Nozomi Entertainment"),
+            Pair("218", "Zexcs"),
+            Pair("220", "Youmex"),
+            Pair("222", "Jade Animation"),
+            Pair("224", "Toshiba EMI"),
+            Pair("226", "Panmedia"),
+            Pair("228", "Daewon Media"),
+            Pair("229", "The Answer Studio"),
+            Pair("230", "Bandai"),
+            Pair("231", "CyberConnect2"),
+            Pair("233", "Bandai Entertainment"),
+            Pair("234", "Oh! Production"),
+            Pair("235", "Shirogumi"),
+            Pair("236", "YTV"),
+            Pair("238", "AT-X"),
+            Pair("239", "Y.O.U.C"),
+            Pair("240", "Capcom"),
+            Pair("241", "Koei"),
+            Pair("242", "Ashi Productions"),
+            Pair("244", "D3"),
+            Pair("245", "Toho Company"),
+            Pair("246", "Visual 80"),
+            Pair("247", "Shin-Ei Animation"),
+            Pair("248", "Tin House"),
+            Pair("249", "Robot"),
+            Pair("250", "Media Blasters"),
+            Pair("252", "4Kids Entertainment"),
+            Pair("253", "Rikuentai"),
+            Pair("254", "Soft Garage"),
+            Pair("255", "Imagi"),
+            Pair("256", "Studio Kikan"),
+            Pair("257", "Jam"),
+            Pair("258", "Echo"),
+            Pair("259", "Kaeruotoko Shokai"),
+            Pair("260", "Shuuhei Morita"),
+            Pair("261", "San-X"),
+            Pair("262", "Kadokawa Pictures USA"),
+            Pair("265", "Valkyria"),
+            Pair("266", "Natural High"),
+            Pair("267", "Five Ways"),
+            Pair("268", "Hot Bear"),
+            Pair("269", "DiC Entertainment"),
+            Pair("270", "NuTech Digital"),
+            Pair("271", "Barnum Studio"),
+            Pair("272", "Studio Kajino"),
+            Pair("273", "Think Corporation"),
+            Pair("274", "Suzuki Mirano"),
+            Pair("275", "Kojiro Shishido Animation Works"),
+            Pair("276", "DLE"),
+            Pair("277", "Anime Midstream"),
+            Pair("278", "Daiei"),
+            Pair("279", "Skouras"),
+            Pair("280", "Studio Junio"),
+            Pair("281", "Kokusai Eigasha"),
+            Pair("282", "Gentosha Comics"),
+            Pair("283", "KENMedia"),
+            Pair("284", "Central Park Media"),
+            Pair("285", "Four Some"),
+            Pair("287", "David Production"),
+            Pair("288", "Kaname Productions"),
+            Pair("289", "ACiD FiLM"),
+            Pair("290", "Kinema Citrus"),
+            Pair("291", "CoMix Wave Films"),
+            Pair("292", "AIC Plus+"),
+            Pair("293", "Picograph"),
+            Pair("294", "Discovery"),
+            Pair("296", "Critical Mass Video"),
+            Pair("297", "Armor"),
+            Pair("298", "Studio Anima"),
+            Pair("299", "Studio Pastoral"),
+            Pair("300", "Silver Link."),
+            Pair("301", "Langmaor"),
+            Pair("302", "GODxDOG Production"),
+            Pair("304", "Primastea"),
+            Pair("306", "Magic Capsule"),
+            Pair("307", "Sunwoo Entertainment"),
+            Pair("308", "Yellow Film"),
+            Pair("309", "GoHands"),
+            Pair("310", "Animeigo"),
+            Pair("311", "Enoki Films"),
+            Pair("312", "Life Work"),
+            Pair("313", "Omnibus Japan"),
+            Pair("314", "White Fox"),
+            Pair("315", "DAX Production"),
+            Pair("316", "Nippon Shuppan Hanbai (Nippan) K.K."),
+            Pair("318", "WAO World"),
+            Pair("319", "MS Pictures"),
+            Pair("320", "Maru Production"),
+            Pair("321", "Kitty Media"),
+            Pair("322", "Bee Media"),
+            Pair("323", "Nippon Columbia"),
+            Pair("324", "Directions"),
+            Pair("325", "Code"),
+            Pair("326", "Animation 21"),
+            Pair("327", "Takara"),
+            Pair("328", "Casio Entertainment"),
+            Pair("329", "Phoenix Entertainment"),
+            Pair("330", "Kanaban Graphics"),
+            Pair("331", "Indeprox"),
+            Pair("332", "Takeshobo"),
+            Pair("333", "TYO Animations"),
+            Pair("334", "Ordet"),
+            Pair("335", "Seta Corporation"),
+            Pair("336", "JM animation"),
+            Pair("338", "Studio Bogey"),
+            Pair("339", "Rankin/Bass"),
+            Pair("340", "Future Planet"),
+            Pair("341", "SamBakZa"),
+            Pair("342", "Japan Home Video"),
+            Pair("343", "Puzzle Animation Studio Limited"),
+            Pair("344", "Atlus"),
+            Pair("345", "TAKI Corporation"),
+            Pair("346", "Hoods Entertainment"),
+            Pair("347", "PrimeTime"),
+            Pair("348", "Kawamoto Productions"),
+            Pair("349", "Itasca Studio"),
+            Pair("350", "The Village of Marchen"),
+            Pair("351", "Big West"),
+            Pair("352", "Kadokawa Pictures Japan"),
+            Pair("353", "Chungeorahm Film"),
+            Pair("354", "Encourage Films"),
+            Pair("356", "Cranberry"),
+            Pair("357", "Innocent Grey"),
+            Pair("358", "Telescreen BV"),
+            Pair("359", "NHK-BS2"),
+            Pair("361", "Oxybot"),
+            Pair("362", "Film Workshop"),
+            Pair("363", "Topcraft"),
+            Pair("364", "Sovat Theater"),
+            Pair("365", "PoRO"),
+            Pair("366", "Tomoyasu Murata Company"),
+            Pair("367", "Heewon Entertainment"),
+            Pair("368", "UM Productions"),
+            Pair("370", "Iyasakadou Film"),
+            Pair("371", "Union Cho"),
+            Pair("372", "NIS America, Inc."),
+            Pair("373", "Panda Factory"),
+            Pair("374", "Plus Heads"),
+            Pair("375", "I.Toon"),
+            Pair("376", "Sentai Filmworks"),
+            Pair("377", "Kuri Jikken Manga Kobo"),
+            Pair("378", "Grasshoppa!"),
+            Pair("379", "Avaco Creative Studios"),
+            Pair("380", "Amuse Pictures"),
+            Pair("381", "Yamato Works"),
+            Pair("382", "Tokuma Shoten"),
+            Pair("383", "KMMJ Studios"),
+            Pair("384", "Trick Block"),
+            Pair("385", "Tele-Cartoon Japan"),
+            Pair("386", "Animaruya"),
+            Pair("387", "Blue Eyes"),
+            Pair("388", "Tamura Shigeru Studio"),
+            Pair("389", "I-move"),
+            Pair("390", "Tsuchida Productions"),
+            Pair("391", "Grouper Productions"),
+            Pair("392", "Enterbrain"),
+            Pair("393", "Epoch"),
+            Pair("394", "Misseri Studio"),
+            Pair("395", "Oz"),
+            Pair("397", "Bridge"),
+            Pair("398", "Milky Cartoon"),
+            Pair("399", "Dream Force"),
+            Pair("400", "Gathering"),
+            Pair("401", "ChuChu"),
+            Pair("402", "ChiChinoya"),
+            Pair("403", "Tokyo Media Connections"),
+            Pair("404", "Studio Unicorn"),
+            Pair("405", "T-Rex"),
+            Pair("406", "Asahi Production"),
+            Pair("407", "Studio 9 MAiami"),
+            Pair("408", "Amino"),
+            Pair("409", "Studio Egg"),
+            Pair("410", "Adult Source Media"),
+            Pair("411", "KBS"),
+            Pair("412", "Studio Wombat"),
+            Pair("413", "Pastel"),
+            Pair("414", "Lemon Heart"),
+            Pair("415", "Warner Bros."),
+            Pair("416", "TAP"),
+            Pair("417", "Walt Disney Studios"),
+            Pair("418", "Studio Gokumi"),
+            Pair("419", "ElectromagneticWave"),
+            Pair("420", "ACC Production"),
+            Pair("421", "International Digital Artist"),
+            Pair("422", "Schoolzone"),
+            Pair("423", "Studio Eromatick"),
+            Pair("424", "Mary Jane"),
+            Pair("425", "AIC Takarazuka"),
+            Pair("426", "Filmlink International"),
+            Pair("427", "Studio G-1Neo"),
+            Pair("428", "Nichiei Agency"),
+            Pair("429", "Office AO"),
+            Pair("430", "Mad Box"),
+            Pair("431", "Procidis"),
+            Pair("432", "Kachidoki Studio"),
+            Pair("433", "View Works"),
+            Pair("434", "Synergy Japan"),
+            Pair("435", "Studio Zero"),
+            Pair("436", "AIC Build"),
+            Pair("437", "Kamikaze Douga"),
+            Pair("438", "Studio Signal"),
+            Pair("439", "Project No.9"),
+            Pair("440", "Studio Kuma"),
+            Pair("441", "8bit"),
+            Pair("442", "Minami Machi Bugyousho"),
+            Pair("443", "Studio PuYUKAI"),
+            Pair("444", "Jinnis Animation Studios"),
+            Pair("445", "Bliss Pictures"),
+            Pair("446", "RTHK"),
+            Pair("447", "Hero Communication"),
+            Pair("448", "Postgal Workshop"),
+            Pair("449", "Maikaze"),
+            Pair("450", "Wako Productions"),
+            Pair("451", "Cookie Jar Entertainment"),
+            Pair("452", "Knack Productions"),
+            Pair("453", "Tsuburaya Productions"),
+            Pair("455", "Palm Studio"),
+            Pair("456", "Lerche"),
+            Pair("457", "Venet"),
+            Pair("458", "Beijing Sharaku Art"),
+            Pair("459", "Nitroplus"),
+            Pair("460", "KlockWorx"),
+            Pair("461", "OLE-M"),
+            Pair("462", "Chaos Project"),
+            Pair("463", "M.S.C"),
+            Pair("464", "flying DOG"),
+            Pair("465", "Hasbro"),
+            Pair("466", "Digital Media Lab"),
+            Pair("467", "Discotek"),
+            Pair("468", "Nihon Falcom"),
+            Pair("469", "ING"),
+            Pair("470", "GAGA Communications"),
+            Pair("471", "Shanghai Animation Film Studio"),
+            Pair("472", "Usagi.Ou"),
+            Pair("473", "Ascension"),
+            Pair("474", "Shogakukan Music & Digital Entertainment"),
+            Pair("475", "Ishikawa Pro"),
+            Pair("476", "The Berich"),
+            Pair("477", "Kino Production"),
+            Pair("478", "Studio Blanc"),
+            Pair("479", "DOGA Productions"),
+            Pair("481", "Office Take Off"),
+            Pair("482", "Automatic Flowers Studio"),
+            Pair("483", "Marvel Entertainment"),
+            Pair("484", "TOKYOPOP"),
+            Pair("485", "Shinwon Productions"),
+            Pair("486", "Digital Frontier"),
+            Pair("487", "Studio MWP"),
+            Pair("488", "Anchor Bay Films"),
+            Pair("489", "TCJ"),
+            Pair("490", "Maiden Japan"),
+            Pair("491", "Byakuya Shobo"),
+            Pair("492", "Horannabi"),
+            Pair("493", "Aniplex of America"),
+            Pair("494", "Studio Take Off"),
+            Pair("495", "Moonstone Cherry"),
+            Pair("497", "Studio Gram"),
+            Pair("499", "The Pokemon Company International"),
+            Pair("501", "Miramax Films"),
+            Pair("503", "Nintendo"),
+            Pair("505", "Nintendo of America"),
+            Pair("507", "Universal Studios"),
+            Pair("509", "Fuji Video"),
+            Pair("511", "Wonder Kids"),
+            Pair("513", "Nikkatsu"),
+            Pair("515", "Fairy Dust"),
+            Pair("517", "Asmik Ace Entertainment"),
+            Pair("519", "Collaboration Works"),
+            Pair("521", "Anpro"),
+            Pair("523", "SBS TV Production"),
+            Pair("525", "KAGAYA Studio"),
+            Pair("527", "Higa Brothers Production"),
+            Pair("529", "Lapis"),
+            Pair("531", "Elevenarts"),
+            Pair("533", "N&G Production"),
+            Pair("535", "Knack Animation"),
+            Pair("537", "SANZIGEN"),
+            Pair("539", "Ultra Super Pictures"),
+            Pair("541", "Seven"),
+            Pair("543", "Studio Saki Makura"),
+            Pair("545", "Studio Deva Loka"),
+            Pair("547", "Hobby Japan"),
+            Pair("549", "StudioRF Inc."),
+            Pair("551", "Crossphere"),
+            Pair("553", "Marvy Jack"),
+            Pair("555", "Studio Chizu"),
+            Pair("557", "Zyc"),
+            Pair("559", "Purple Cow Studio Japan"),
+            Pair("561", "U/M/A/A Inc."),
+            Pair("563", "LandQ studios"),
+            Pair("565", "LMD"),
+            Pair("567", "D.A.S.T."),
+            Pair("569", "MAPPA"),
+            Pair("571", "Obtain Future"),
+            Pair("573", "JapanAnime"),
+            Pair("575", "Katsudou-manga-kan"),
+            Pair("577", "Tohokushinsha Film Corporation"),
+            Pair("579", "AT-2"),
+            Pair("581", "Sparky Animation"),
+            Pair("583", "Annapuru"),
+            Pair("585", "Arplants"),
+            Pair("587", "Tomason"),
+            Pair("589", "Academy Productions"),
+            Pair("591", "Romanov Films"),
+            Pair("593", "Egg"),
+            Pair("595", "NYAV Post"),
+            Pair("601", "ixtl"),
+            Pair("605", "C2C"),
+            Pair("607", "RAMS"),
+            Pair("609", "Cammot"),
+            Pair("611", "Sakura Production"),
+            Pair("613", "Jormungand Production Partners"),
+            Pair("615", "Dream Creation"),
+            Pair("619", "Cospa"),
+            Pair("621", "SoftBank Creative Corp."),
+            Pair("623", "Kimi To Boku Production Partners"),
+            Pair("627", "Madoka Partners"),
+            Pair("629", "Studio Sign"),
+            Pair("633", "Tasogare Otome×Amnesia Production Partners"),
+            Pair("637", "Möbius Tone"),
+            Pair("643", "Trinity Sound"),
+            Pair("645", "Audio Highs"),
+            Pair("647", "Memory-Tech"),
+            Pair("649", "Hakoniwa Academy Student Council"),
+            Pair("653", "tsuritama partners"),
+            Pair("655", "Volks"),
+            Pair("657", "Lucky Paradise"),
+            Pair("661", "Right Gauge"),
+            Pair("663", "Dwango Music Entertainment"),
+            Pair("665", "chara-ani.com"),
+            Pair("667", "AC Create"),
+            Pair("675", "Yomiuri Advertising"),
+            Pair("677", "Nexon"),
+            Pair("679", "Soyuzmultfilm"),
+            Pair("681", "ASCII Media Works"),
+            Pair("685", "Kadokawa Contents Gate"),
+            Pair("687", "Bandai Namco Live Creative"),
+            Pair("689", "NTT Docomo"),
+            Pair("691", "501st JOINT FIGHTER WING"),
+            Pair("693", "BS-TBS"),
+            Pair("697", "Natsuiro Kiseki Production Partners"),
+            Pair("699", "feng"),
+            Pair("701", "seikaisha"),
+            Pair("703", "Notes"),
+            Pair("705", "Project Railgun"),
+            Pair("707", "TMS-Kyokuchi"),
+            Pair("709", "PPP"),
+            Pair("711", "Delfi Sound"),
+            Pair("713", "12 Diary Holders"),
+            Pair("715", "Dwango"),
+            Pair("717", "TV Tokyo Music"),
+            Pair("719", "Studio Mausu"),
+            Pair("721", "GANSIS"),
+            Pair("723", "Hotline"),
+            Pair("727", "Kiyosumi High School Mahjong Club"),
+            Pair("729", "Raku High Student Council"),
+            Pair("731", "Inu x Boku SS Production Partners"),
+            Pair("733", "Viki"),
+            Pair("735", "Slowcurve"),
+            Pair("737", "Sony Music Communications"),
+            Pair("739", "Project IS"),
+            Pair("741", "Toshiba Entertainment"),
+            Pair("743", "Project Eureka AO"),
+            Pair("745", "Cosmic Ray"),
+            Pair("747", "Apollon"),
+            Pair("751", "Marvelous AQL"),
+            Pair("753", "PRA"),
+            Pair("755", "Jumondo"),
+            Pair("757", "Sony Music Entertainment"),
+            Pair("759", "Eye Move"),
+            Pair("761", "Sunny Side Up"),
+            Pair("763", "Miracle Robo"),
+            Pair("765", "Sakura Create"),
+            Pair("767", "Majin"),
+            Pair("769", "Fuji Pacific Music Publishing"),
+            Pair("773", "Peter Pan Creation"),
+            Pair("775", "Bushiroad"),
+            Pair("777", "Showgate"),
+            Pair("779", "AMG MUSIC"),
+            Pair("781", "Studio NOA"),
+            Pair("783", "GKids"),
+            Pair("785", "Advance Syakujii"),
+            Pair("787", "Happinet Pictures"),
+            Pair("789", "BIGLOBE"),
+            Pair("791", "Dentsu Entertainment USA"),
+            Pair("793", "Glams"),
+            Pair("795", "Yahoo! Japan"),
+            Pair("797", "Houbunsha"),
+            Pair("799", "indigo line"),
+            Pair("801", "Nihikime no Dozeu"),
+            Pair("803", "Trigger"),
+            Pair("805", "AIC Frontier"),
+            Pair("807", "Po10tial"),
+            Pair("809", "AKOM"),
+            Pair("811", "Hang Zhou StarQ"),
+            Pair("813", "SoftCel Pictures"),
+            Pair("815", "EMI"),
+            Pair("817", "WField"),
+            Pair("819", "Next Media Animation"),
+            Pair("821", "Cotton Doll"),
+            Pair("823", "Lune Pictures"),
+            Pair("825", "Fifth Avenue"),
+            Pair("827", "Amber Film Works"),
+            Pair("829", "Studio Jack"),
+            Pair("831", "Otogi Production"),
+            Pair("833", "Kyotoma"),
+            Pair("835", "UNLIMITED Partners"),
+            Pair("839", "LIDENFILMS"),
+            Pair("841", "Saban Entertainment"),
+            Pair("843", "BS Fuji"),
+            Pair("845", "Project No Name"),
+            Pair("847", "Senran Kagura Partners"),
+            Pair("848", "Kazami Gakuen Koushiki Douga-bu"),
+            Pair("850", "Kenji Studio"),
+            Pair("852", "Nexus"),
+            Pair("854", "Solid Vox"),
+            Pair("856", "Charaction"),
+            Pair("858", "Wit Studio"),
+            Pair("860", "Triple X"),
+            Pair("862", "Pashmina"),
+            Pair("866", "Fanworks"),
+            Pair("870", "Index"),
+            Pair("872", "Shingeki no Kyojin Team"),
+            Pair("874", "Flex Comics"),
+            Pair("876", "G-mode"),
+            Pair("878", "An DerCen"),
+            Pair("880", "gimik"),
+            Pair("882", "Toranoana"),
+            Pair("884", "Strawberry Meets Pictures"),
+            Pair("886", "Cinema Tohoku"),
+            Pair("890", "Karaku"),
+            Pair("892", "NOTTV"),
+            Pair("894", "Graphinica"),
+            Pair("896", "Larx Entertainment"),
+            Pair("898", "Xebec Zwei"),
+            Pair("899", "Tokyo Animation Film"),
+            Pair("901", "Tsubasa Entertainment"),
+            Pair("903", "Studio Zealot"),
+            Pair("905", "Tokuma Japan"),
+            Pair("907", "AYCO"),
+            Pair("909", "Studio Kelmadick"),
+            Pair("911", "Passione"),
+            Pair("913", "Ryukyu Asahi Broadcasting"),
+            Pair("917", "Takahashi Studio"),
+            Pair("919", "Ankama"),
+            Pair("921", "Dynamic Planning"),
+            Pair("923", "CyberStep"),
+            Pair("925", "Earth Star Entertainment"),
+            Pair("929", "Animation Do"),
+            Pair("931", "Poncotan"),
+            Pair("933", "Carp Studio"),
+            Pair("937", "Studio Kaab"),
+            Pair("939", "Tama Production"),
+            Pair("941", "Iwatobi High School Swimming Club"),
+            Pair("945", "Vasoon Animation"),
+            Pair("947", "Manga Entertainment"),
+            Pair("949", "teamKG"),
+            Pair("951", "NAZ"),
+            Pair("953", "Soft on Demand"),
+            Pair("957", "Connect"),
+            Pair("959", "Studio Compile"),
+            Pair("961", "Baramiri"),
+            Pair("963", "MAGES."),
+            Pair("965", "Crimson Star Media"),
+            Pair("971", "Anime R"),
+            Pair("973", "Studio Liberty"),
+            Pair("975", "Angelfish"),
+            Pair("977", "Queen Bee"),
+            Pair("979", "SPEED"),
+            Pair("981", "BOOTLEG"),
+            Pair("983", "Daiko"),
+            Pair("985", "P Productions"),
+            Pair("987", "Creative Bridge"),
+            Pair("989", "Nihon Hoso Eigasha"),
+            Pair("991", "Hoso Seisaku Doga"),
+            Pair("995", "Coamix"),
+            Pair("997", "Maxell E-Cube"),
+            Pair("999", "Studio Animal"),
+            Pair("1001", "Kyodo Eiga"),
+            Pair("1003", "Nippon Television Network Corporation"),
+            Pair("1005", "Oddjob"),
+            Pair("1009", "Aubec"),
+            Pair("1011", "Warner Music Japan"),
+            Pair("1013", "Opera House"),
+            Pair("1015", "T.O Entertainment"),
+            Pair("1017", "Liverpool"),
+            Pair("1019", "Himeyuri Alumnae Incorporated Foundation"),
+            Pair("1021", "ASIA Documentary Productions"),
+            Pair("1023", "Polygon Pictures"),
+            Pair("1025", "Dongwoo A&E"),
+            Pair("1027", "Milkshake"),
+            Pair("1029", "BeeWorks"),
+            Pair("1031", "Universal Radio Studio"),
+            Pair("1033", "Studio Colorido"),
+            Pair("1037", "Japan Vistec"),
+            Pair("1039", "DIVE II Entertainment"),
+            Pair("1041", "Ai Addiction"),
+            Pair("1043", "Benesse Corporation"),
+            Pair("1045", "Space Neko Company Ltd."),
+            Pair("1049", "PoPoCo"),
+            Pair("1053", "Production IMS"),
+            Pair("1055", "Yamamura Animation, Inc."),
+            Pair("1057", "Kyowa Film"),
+            Pair("1059", "Studio Nem"),
+            Pair("1061", "Project Team Eikyuu Kikan"),
+            Pair("1063", "Calf Studio"),
+            Pair("1065", "Public & Basic"),
+            Pair("1067", "Rising Force"),
+            Pair("1071", "Studio Lotus"),
+            Pair("1073", "Magic Lantern Film"),
+            Pair("1075", "C-Station"),
+            Pair("1077", "MMDGP"),
+            Pair("1079", "3xCube"),
+            Pair("1081", "ZERO-A"),
+            Pair("1083", "Aikikaku Center"),
+            Pair("1085", "Cosmos"),
+            Pair("1087", "Lay-duce"),
+            Pair("1089", "Studio Zain"),
+            Pair("1091", "Gakken Eigakyoku"),
+            Pair("1093", "BreakBottle"),
+            Pair("1097", "Namco Bandai Games"),
+            Pair("1099", "Cyclone Graphics inc"),
+            Pair("1101", "Lambert"),
+            Pair("1103", "TROYCA"),
+            Pair("1105", "Gendai Production"),
+            Pair("1107", "Rabbit Gate"),
+            Pair("1109", "Orange"),
+            Pair("1111", "DandeLion Animation Studio LLC"),
+            Pair("1113", "NBCUniversal Entertainment Japan"),
+            Pair("1115", "Studio Korumi"),
+            Pair("1117", "1st PLACE"),
+            Pair("1119", "Shuka"),
+            Pair("1121", "Banpresto"),
+            Pair("1123", "Lucent Pictures Entertainment"),
+            Pair("1127", "Studio 3Hz"),
+            Pair("1129", "Pierrot Plus"),
+            Pair("1131", "Zero-G Room"),
+            Pair("1133", "dwarf"),
+            Pair("1135", "FOREST Hunting One"),
+            Pair("1137", "SEK Studios"),
+            Pair("1139", "BEAM Entertainment"),
+            Pair("1141", "Comstock, Ltd."),
+            Pair("1143", "TOHO animation"),
+            Pair("1147", "Shinano Kikaku"),
+            Pair("1149", "Studio Cab"),
+            Pair("1151", "Sanrio Digital"),
+            Pair("1153", "Image Kei"),
+            Pair("1155", "Studio Moriken"),
+            Pair("1157", "Big Bang"),
+            Pair("1159", "King Bee"),
+            Pair("1161", "Allure"),
+            Pair("1163", "Flatiron Film Company"),
+            Pair("1165", "Tri-Slash"),
+            Pair("1167", "Nikkatsu Mukojima"),
+            Pair("1169", "ILCA"),
+            Pair("1171", "Olive Studio"),
+            Pair("1173", "Studio Dadashow"),
+            Pair("1175", "Atelier Musa"),
+            Pair("1177", "Geijutsu Eigasha"),
+            Pair("1179", "MK Pictures"),
+            Pair("1181", "RG Animation Studios"),
+            Pair("1185", "81 Produce"),
+            Pair("1187", "Ripple Film"),
+            Pair("1189", "Sola Digital Arts"),
+            Pair("1191", "Azeta Pictures"),
+            Pair("1193", "MooGoo"),
+            Pair("1195", "Creators in Pack"),
+            Pair("1199", "L."),
+            Pair("1201", "Ponycan USA"),
+            Pair("1203", "Studio 1st"),
+            Pair("1207", "Comic Umenohone"),
+            Pair("1209", "Studio A-CAT"),
+            Pair("1211", "Tokyo MX"),
+            Pair("1213", "Mobcast"),
+            Pair("1215", "Daiichikosho"),
+            Pair("1217", "HeART-BIT"),
+            Pair("1219", "Hokkaido Azmacy"),
+            Pair("1221", "Hokkaido Cultural Broadcasting"),
+            Pair("1223", "Teichiku Entertainment"),
+            Pair("1225", "Age Global Networks"),
+            Pair("1227", "Studio GDW"),
+            Pair("1229", "Pie in The Sky"),
+            Pair("1231", "OLM Digital"),
+            Pair("1233", "Bandai Namco Entertainment"),
+            Pair("1235", "I was a Ballerina"),
+            Pair("1237", "Millepensee"),
+            Pair("1239", "KeyEast"),
+            Pair("1241", "Evil Line Records"),
+            Pair("1242", "Sakura Color Film"),
+            Pair("1243", "AniMan"),
+            Pair("1244", "Studio VOLN"),
+            Pair("1245", "ZIZ Entertainment (ZIZ)"),
+            Pair("1246", "AIR AGENCY"),
+            Pair("1247", "Mistral Japan"),
+            Pair("1248", "Studio Mirai"),
+            Pair("1249", "Mirai Film"),
+            Pair("1250", "The National Film Center Japan"),
+            Pair("1251", "Dentsu Eigasha Tokyo"),
+            Pair("1252", "Studio Binzo"),
+            Pair("1253", "Studio GOONEYS"),
+            Pair("1254", "Grooove"),
+            Pair("1255", "Glovision"),
+            Pair("1256", "Harappa"),
+            Pair("1257", "A-Real"),
+            Pair("1258", "Bandai Namco Pictures"),
+            Pair("1259", "Studio Curtain"),
+            Pair("1260", "Nakamura Production"),
+            Pair("1261", "Good Smile Company"),
+            Pair("1263", "Dynamo Pictures"),
+            Pair("1264", "EMT²"),
+            Pair("1265", "Tomovies"),
+            Pair("1266", "Studio! Cucuri"),
+            Pair("1268", "L²Studio"),
+            Pair("1269", "K-Factory"),
+            Pair("1270", "Sanctuary"),
+            Pair("1271", "Assez Finaud Fabric"),
+            Pair("1273", "Osaka University of Arts"),
+            Pair("1276", "HS Pictures Studio"),
+            Pair("1277", "Imagica West"),
+            Pair("1278", "Signal. MD"),
+            Pair("1279", "Buemon"),
+            Pair("1280", "Media Castle"),
+            Pair("1281", "Office DCI"),
+            Pair("1282", "Arcturus"),
+            Pair("1283", "TC Entertainment"),
+            Pair("1284", "Avex Pictures"),
+            Pair("1285", "Being"),
+            Pair("1286", "10Gauge"),
+            Pair("1287", "Q-Tec"),
+            Pair("1288", "Chrono Gear Creative"),
+            Pair("1289", "F.M.F"),
+            Pair("1290", "Pollyanna Graphics"),
+            Pair("1291", "KOO-KI"),
+            Pair("1292", "TUBA"),
+            Pair("1293", "Romantica club !!"),
+            Pair("1294", "Studio Don Juan"),
+            Pair("1295", "Pine Jam"),
+            Pair("1296", "Marza Animation Planet"),
+            Pair("1298", "Sakura Motion Picture"),
+            Pair("1299", "AXsiZ"),
+            Pair("1300", "Office Nobu"),
+            Pair("1301", "CCTV Animation Co. LTD"),
+            Pair("1302", "RoiVisual"),
+            Pair("1303", "PansonWorks"),
+            Pair("1304", "Qualia Animation"),
+            Pair("1305", "Milestone Music Publishing"),
+            Pair("1306", "AIC Classic"),
+            Pair("1307", "Free-Will"),
+            Pair("1308", "SEDIC International"),
+            Pair("1309", "Lawson HMV Entertainment"),
+            Pair("1310", "Hiroshi Planning"),
+            Pair("1311", "Sankyo Planning"),
+            Pair("1312", "Tokyu Recreation"),
+            Pair("1313", "Amuse"),
+            Pair("1314", "Fukushima Gainax"),
+            Pair("1315", "Fujiko F. Fujio Pro"),
+            Pair("1316", "Trans Cosmos"),
+            Pair("1317", "Production GoodBook"),
+            Pair("1318", "Asahi Shimbun"),
+            Pair("1319", "Tokyo Theatres Company"),
+            Pair("1320", "Joker Films"),
+            Pair("1322", "Creative Power Entertaining"),
+            Pair("1323", "Saban Brands"),
+            Pair("1324", "Shimogumi"),
+            Pair("1325", "Haoliners Animation League"),
+            Pair("1326", "Bitgang"),
+            Pair("1327", "White Bear"),
+            Pair("1328", "Decovocal"),
+            Pair("1329", "AQUAPLUS"),
+            Pair("1330", "ensky"),
+            Pair("1331", "i0+"),
+            Pair("1332", "famima.com"),
+            Pair("1333", "Hakuhodo DY Music & Pictures"),
+            Pair("1334", "Docomo Anime Store"),
+            Pair("1335", "TEAM Entertainment Inc."),
+            Pair("1336", "Chugai Mining Co., Ltd."),
+            Pair("1337", "Medicos Entertainment"),
+            Pair("1338", "Diabolik Lovers MB Project"),
+            Pair("1341", "Beijing Sunchime Happy Culture Company"),
+            Pair("1343", "Space Shower Music"),
+            Pair("1344", "King Records"),
+            Pair("1345", "Sammy"),
+            Pair("1346", "Kinoshita Koumuten"),
+            Pair("1347", "Hobi Animation"),
+            Pair("1348", "Sugar Boy"),
+            Pair("1349", "Tencent Animation"),
+            Pair("1350", "G.CMay Animation & Film"),
+            Pair("1351", "EDGE"),
+            Pair("1352", "Studio Ten Carat"),
+            Pair("1353", "Issen"),
+            Pair("1354", "Penta Show Studios"),
+            Pair("1355", "TV Aichi"),
+            Pair("1356", "Tonko House"),
+            Pair("1357", "Craftar"),
+            Pair("1358", "Fields"),
+            Pair("1359", "Shizuoka Broadcasting System"),
+            Pair("1360", "Hoods Drifters Studio"),
+            Pair("1361", "Darts"),
+            Pair("1362", "Rabbit Machine"),
+            Pair("1363", "Marine Entertainment"),
+            Pair("1364", "Super Techno Arts"),
+            Pair("1365", "Shueisha"),
+            Pair("1366", "Nagoya TV Housou"),
+            Pair("1368", "Kazuki Production"),
+            Pair("1369", "G-Lam"),
+            Pair("1370", "Sprite Animation Studios"),
+            Pair("1371", "T.P.O"),
+            Pair("1372", "SOEISHINSHA"),
+            Pair("1373", "Akita Shoten"),
+            Pair("1374", "Office Takeout"),
+            Pair("1375", "Studio Jam"),
+            Pair("1376", "ZOOM ENTERPRISE"),
+            Pair("1377", "Futabasha"),
+            Pair("1378", "Media Rings"),
+            Pair("1379", "Zero-G"),
+            Pair("1380", "domerica"),
+            Pair("1381", "GEMBA"),
+            Pair("1382", "Piko Studio"),
+            Pair("1383", "Studio Live"),
+            Pair("1384", "CinePix"),
+            Pair("1385", "Blue Cat"),
+            Pair("1386", "Infinite"),
+            Pair("1387", "Neft Film"),
+            Pair("1389", "Frencel"),
+            Pair("1391", "SPO Entertainment"),
+            Pair("1392", "Zack Promotion"),
+            Pair("1393", "Geno Studio"),
+            Pair("1394", "RightTracks"),
+            Pair("1395", "Clarion"),
+            Pair("1396", "W-Toon Studio"),
+            Pair("1397", "Universal Music Japan"),
+            Pair("1398", "Akabanten"),
+            Pair("1399", "Asura Film"),
+            Pair("1400", "Walkers Company"),
+            Pair("1401", "Amgakuin"),
+            Pair("1403", "Meiji Seika"),
+            Pair("1404", "PSG"),
+            Pair("1405", "Chuubu Nihon Kyouei"),
+            Pair("1406", "Miracle Bus"),
+            Pair("1408", "Lastrum Music"),
+            Pair("1409", "Monomusik"),
+            Pair("1410", "Rambling Records"),
+            Pair("1411", "Kyoraku Industrial Holdings"),
+            Pair("1412", "Kansai Telecasting Corporation"),
+            Pair("1414", "bilibili"),
+            Pair("1415", "Asahi Broadcasting Corporation"),
+            Pair("1416", "BS11"),
+            Pair("1417", "RAB Aomori Broadcasting Corporation"),
+            Pair("1418", "Nippon Television Music Corporation"),
+            Pair("1419", "GARDEN LODGE"),
+            Pair("1420", "SIDO LIMITED"),
+            Pair("1421", "Takara Tomy A.R.T.S"),
+            Pair("1422", "CyberAgent"),
+            Pair("1423", "Forecast Communications"),
+            Pair("1424", "Media Bank"),
+            Pair("1425", "5pb."),
+            Pair("1426", "Studio Meditation With a Pencil"),
+            Pair("1427", "EBS"),
+            Pair("1428", "Level-5"),
+            Pair("1429", "Azumaker"),
+            Pair("1430", "Shogakukan"),
+            Pair("1431", "Yaoqi"),
+            Pair("1432", "Bishop"),
+            Pair("1433", "Ekura Animal"),
+            Pair("1435", "JCF"),
+            Pair("1436", "Sting Ray"),
+            Pair("1437", "Ishimori Entertainment"),
+            Pair("1438", "Medicrie"),
+            Pair("1439", "Digiturbo"),
+            Pair("1440", "Felix Film"),
+            Pair("1441", "Mippei Eigeki Kiryuukan"),
+            Pair("1442", "Mook DLE"),
+            Pair("1443", "Overlap"),
+            Pair("1444", "Egg Firm"),
+            Pair("1445", "Arcs Create"),
+            Pair("1446", "NEC Avenue"),
+            Pair("1447", "Urban Vision"),
+            Pair("1448", "Network"),
+            Pair("1449", "Animatic"),
+            Pair("1450", "Universal Pictures Japan"),
+            Pair("1451", "TYO"),
+            Pair("1452", "Mag Garden"),
+            Pair("1454", "REALTHING"),
+            Pair("1455", "Tokai Television"),
+            Pair("1456", "Beijing Huihuang Animation Company"),
+            Pair("1457", "Hisashishi Videos"),
+            Pair("1458", "TOMY Company"),
+            Pair("1459", "Geneon Entertainment USA"),
+            Pair("1460", "Coastline Animation Studio"),
+            Pair("1461", "Audio Tanaka"),
+            Pair("1462", "B&T"),
+            Pair("1463", "Anime Antenna Iinkai"),
+            Pair("1464", "Hiro Media"),
+            Pair("1465", "Enlight Pictures"),
+            Pair("1466", "Bandai Visual USA"),
+            Pair("1467", "Pied Piper"),
+            Pair("1468", "Crunchyroll"),
+            Pair("1469", "BS Japan"),
+            Pair("1470", "Konami Digital Entertainment"),
+            Pair("1471", "Platinum Vision"),
+            Pair("1472", "Myung Films"),
+            Pair("1473", "Odolttogi"),
+            Pair("1474", "Synch-Point"),
+            Pair("1476", "Shochiku Animation Institute"),
+            Pair("1477", "Ministry of the Navy"),
+            Pair("1478", "Pencil"),
+            Pair("1479", "Studio Core"),
+            Pair("1480", "CBS"),
+            Pair("1481", "comico"),
+            Pair("1483", "Fuji&gumi Games"),
+            Pair("1484", "Cinelicious Pics"),
+            Pair("1485", "Light Chaser Animation Studios"),
+            Pair("1486", "eigoMANGA"),
+            Pair("1487", "Bouncy"),
+            Pair("1488", "Hakuhodo DY Media Partners"),
+        )
+    }
+}
diff --git a/src/uk/uakino/ic_launcher-playstore.png b/src/uk/uakino/ic_launcher-playstore.png
deleted file mode 100644
index 0df13731..00000000
Binary files a/src/uk/uakino/ic_launcher-playstore.png and /dev/null differ
diff --git a/src/uk/ufdub/ic_launcher-playstore.png b/src/uk/ufdub/ic_launcher-playstore.png
deleted file mode 100644
index 77a1ef3c..00000000
Binary files a/src/uk/ufdub/ic_launcher-playstore.png and /dev/null differ
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 {