diff --git a/src/all/netflixmirror/AndroidManifest.xml b/src/all/netflixmirror/AndroidManifest.xml
deleted file mode 100644
index 8072ee00d..000000000
--- a/src/all/netflixmirror/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/src/all/netflixmirror/build.gradle b/src/all/netflixmirror/build.gradle
deleted file mode 100644
index 21044713a..000000000
--- a/src/all/netflixmirror/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-plugins {
- alias(libs.plugins.android.application)
- alias(libs.plugins.kotlin.android)
- alias(libs.plugins.kotlin.serialization)
-}
-
-ext {
- extName = 'NetFlix Mirror'
- pkgNameSuffix = 'all.netflixmirror'
- extClass = '.NetFlixMirror'
- extVersionCode = 1
-}
-
-dependencies {
- implementation(project(':lib-playlist-utils'))
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/all/netflixmirror/res/mipmap-hdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 68e5ac658..000000000
Binary files a/src/all/netflixmirror/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/netflixmirror/res/mipmap-mdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index b5e1811ca..000000000
Binary files a/src/all/netflixmirror/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/netflixmirror/res/mipmap-xhdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 838d63e3a..000000000
Binary files a/src/all/netflixmirror/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/netflixmirror/res/mipmap-xxhdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index f9599bf70..000000000
Binary files a/src/all/netflixmirror/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/netflixmirror/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index ca41fc42a..000000000
Binary files a/src/all/netflixmirror/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/netflixmirror/res/web_hi_res_512.png b/src/all/netflixmirror/res/web_hi_res_512.png
deleted file mode 100644
index fd4971093..000000000
Binary files a/src/all/netflixmirror/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/CookieInterceptor.kt b/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/CookieInterceptor.kt
deleted file mode 100644
index 01fd66e7e..000000000
--- a/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/CookieInterceptor.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.all.netflixmirror
-
-import android.util.Log
-import android.webkit.CookieManager
-import okhttp3.Interceptor
-import okhttp3.Response
-
-class CookieInterceptor(
- private val domain: String,
- private val key: String,
- private val value: String,
-) : Interceptor {
-
- init {
- val url = "https://$domain/"
- val cookie = "$key=$value; Domain=$domain; Path=/"
- setCookie(url, cookie)
- }
-
- override fun intercept(chain: Interceptor.Chain): Response {
- val request = chain.request()
- if (!request.url.host.endsWith(domain)) return chain.proceed(request)
-
- val cookie = "$key=$value"
- val cookieList = request.header("Cookie")?.split("; ") ?: emptyList()
- if (cookie in cookieList) return chain.proceed(request)
-
- setCookie("https://$domain/", "$cookie; Domain=$domain; Path=/")
- val prefix = "$key="
- val newCookie = buildList(cookieList.size + 1) {
- cookieList.filterNotTo(this) { it.startsWith(prefix) }
- add(cookie)
- }.joinToString("; ")
- val newRequest = request.newBuilder().header("Cookie", newCookie).build()
- return chain.proceed(newRequest)
- }
-
- private fun setCookie(url: String, value: String) {
- try {
- CookieManager.getInstance().setCookie(url, value)
- } catch (e: Exception) {
- Log.e(domain, "failed to set cookie", e)
- }
- }
-}
diff --git a/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/NetFlixMirror.kt b/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/NetFlixMirror.kt
deleted file mode 100644
index fb5ad0e36..000000000
--- a/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/NetFlixMirror.kt
+++ /dev/null
@@ -1,271 +0,0 @@
-package eu.kanade.tachiyomi.animeextension.all.netflixmirror
-
-import android.app.Application
-import androidx.preference.ListPreference
-import androidx.preference.PreferenceScreen
-import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.DetailsDto
-import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.EpisodeUrl
-import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.EpisodesDto
-import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.SearchDto
-import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.SeasonEpisodesDto
-import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.VideoList
-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.playlistutils.PlaylistUtils
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.asObservableSuccess
-import eu.kanade.tachiyomi.util.asJsoup
-import kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.Json
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
-import okhttp3.Request
-import okhttp3.Response
-import org.jsoup.select.Elements
-import rx.Observable
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import uy.kohesive.injekt.injectLazy
-import kotlin.math.min
-
-class NetFlixMirror : AnimeHttpSource(), ConfigurableAnimeSource {
-
- override val name = "NetFlix Mirror"
-
- override val baseUrl = "https://m.netflixmirror.com"
-
- override val lang = "all"
-
- override val supportsLatest = false
-
- private val json: Json by injectLazy()
-
- override val client = network.cloudflareClient.newBuilder()
- .addNetworkInterceptor(
- CookieInterceptor(baseUrl.toHttpUrl().host, "hd", "on"),
- )
- .build()
-
- override fun headersBuilder() = super.headersBuilder()
- .add("Referer", "$baseUrl/")
-
- private val xhrHeaders by lazy {
- headersBuilder()
- .add("X-Requested-With", "XMLHttpRequest")
- .build()
- }
-
- private val preferences by lazy {
- Injekt.get().getSharedPreferences("source_$id", 0x0000)
- }
-
- private val playListUtils by lazy {
- PlaylistUtils(client, headers)
- }
-
- private lateinit var pageElements: Elements
-
- override fun fetchPopularAnime(page: Int): Observable {
- return if (page == 1) {
- super.fetchPopularAnime(page)
- } else {
- Observable.just(paginatedAnimePageParse(page))
- }
- }
-
- override fun popularAnimeRequest(page: Int): Request {
- return GET("$baseUrl/home", headers)
- }
-
- override fun popularAnimeParse(response: Response): AnimesPage {
- pageElements = response.asJsoup().select("article > a.post-data")
-
- return paginatedAnimePageParse(1)
- }
-
- private fun paginatedAnimePageParse(page: Int): AnimesPage {
- val end = min(page * 20, pageElements.size)
- val entries = pageElements.subList((page - 1) * 20, end).map {
- SAnime.create().apply {
- title = "" // no title here
- url = it.attr("data-post")
- thumbnail_url = it.selectFirst("img")?.attr("abs:data-src")
- }
- }
-
- return AnimesPage(entries, end < pageElements.size)
- }
-
- override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable {
- return if (query.isNotEmpty()) {
- super.fetchSearchAnime(page, query, filters)
- } else {
- if (page == 1) {
- val pageFilter = filters.filterIsInstance().firstOrNull()?.selected ?: "/home"
- val request = GET(baseUrl + pageFilter, headers)
-
- client.newCall(request)
- .asObservableSuccess()
- .map(::popularAnimeParse)
- } else {
- Observable.just(paginatedAnimePageParse(page))
- }
- }
- }
-
- override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
- val url = "$baseUrl/search.php".toHttpUrl().newBuilder().apply {
- addQueryParameter("s", query.trim())
- addQueryParameter("t", System.currentTimeMillis().toString())
- }.build().toString()
-
- return GET(url, xhrHeaders)
- }
-
- override fun getFilterList() = getFilters()
-
- override fun searchAnimeParse(response: Response): AnimesPage {
- val result = response.parseAs()
-
- val entries = result.searchResult?.map {
- SAnime.create().apply {
- url = it.id
- title = it.title
- thumbnail_url = idToThumbnailUrl(it.id)
- }
- } ?: emptyList()
-
- return AnimesPage(entries, false)
- }
-
- override fun animeDetailsRequest(anime: SAnime): Request {
- val url = "$baseUrl/post.php?id=${anime.url}&t=${System.currentTimeMillis()}"
-
- return GET(url, xhrHeaders)
- }
-
- override fun animeDetailsParse(response: Response): SAnime {
- val result = response.parseAs()
- val id = response.request.url.queryParameter("id")!!
-
- return SAnime.create().apply {
- title = result.title
- url = id
- thumbnail_url = idToThumbnailUrl(id)
- genre = "${result.genre}, ${result.cast}"
- author = result.creator
- artist = result.director
- description = result.desc
- if (!result.lang.isNullOrEmpty()) {
- description += "\n\nAvailable Language(s): ${result.lang.joinToString { it.language }}"
- }
- status = result.status
- }
- }
-
- override fun episodeListRequest(anime: SAnime) = animeDetailsRequest(anime)
-
- override fun episodeListParse(response: Response): List {
- val result = response.parseAs()
- val id = response.request.url.queryParameter("id")!!
-
- if (result.episodes?.firstOrNull() == null) {
- return SEpisode.create().apply {
- name = "Movie"
- url = EpisodeUrl(id, result.title).let(json::encodeToString)
- }.let(::listOf)
- }
-
- val episodes = result.episodes.mapNotNull {
- if (it == null) return@mapNotNull null
-
- it.toSEpisode(result.title)
- }.toMutableList()
-
- result.season?.reversed()?.drop(1)?.forEach { season ->
- val seasonRequest = GET("$baseUrl/episodes.php?s=${season.id}&series=$id&t=${System.currentTimeMillis()}", xhrHeaders)
- val seasonResponse = client.newCall(seasonRequest).execute().parseAs()
-
- episodes.addAll(
- index = 0,
- elements = seasonResponse.episodes?.map {
- it.toSEpisode(result.title)
- } ?: emptyList(),
- )
- }
-
- return episodes.reversed()
- }
-
- override fun videoListRequest(episode: SEpisode): Request {
- val episodeUrl = episode.url.parseAs()
-
- val url = "$baseUrl/playlist.php".toHttpUrl().newBuilder().apply {
- addQueryParameter("id", episodeUrl.id)
- addQueryParameter("t", episodeUrl.title)
- addQueryParameter("tm", System.currentTimeMillis().toString())
- }.build().toString()
-
- return GET(url, xhrHeaders)
- }
-
- override fun videoListParse(response: Response): List