diff --git a/src/all/missav/AndroidManifest.xml b/src/all/missav/AndroidManifest.xml
new file mode 100644
index 0000000000..8072ee00db
--- /dev/null
+++ b/src/all/missav/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/src/all/missav/build.gradle b/src/all/missav/build.gradle
new file mode 100644
index 0000000000..2b684e3aa8
--- /dev/null
+++ b/src/all/missav/build.gradle
@@ -0,0 +1,19 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+}
+
+ext {
+ extName = 'MissAV'
+ pkgNameSuffix = 'all.missav'
+ extClass = '.MissAV'
+ extVersionCode = 1
+ containsNsfw = true
+}
+
+dependencies {
+ implementation(project(':lib-unpacker'))
+ implementation(project(':lib-playlist-utils'))
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/all/missav/res/mipmap-hdpi/ic_launcher.png b/src/all/missav/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..0310483009
Binary files /dev/null and b/src/all/missav/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/all/missav/res/mipmap-mdpi/ic_launcher.png b/src/all/missav/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..a76872ae96
Binary files /dev/null and b/src/all/missav/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/all/missav/res/mipmap-xhdpi/ic_launcher.png b/src/all/missav/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..59d6dfdb77
Binary files /dev/null and b/src/all/missav/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/all/missav/res/mipmap-xxhdpi/ic_launcher.png b/src/all/missav/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..09573e1541
Binary files /dev/null and b/src/all/missav/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/all/missav/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/missav/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..ca73427866
Binary files /dev/null and b/src/all/missav/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/all/missav/res/web_hi_res_512.png b/src/all/missav/res/web_hi_res_512.png
new file mode 100644
index 0000000000..2b9568fd79
Binary files /dev/null and b/src/all/missav/res/web_hi_res_512.png differ
diff --git a/src/all/missav/src/eu/kanade/tachiyomi/animeextension/all/missav/MissAV.kt b/src/all/missav/src/eu/kanade/tachiyomi/animeextension/all/missav/MissAV.kt
new file mode 100644
index 0000000000..aa7ef09a72
--- /dev/null
+++ b/src/all/missav/src/eu/kanade/tachiyomi/animeextension/all/missav/MissAV.kt
@@ -0,0 +1,165 @@
+package eu.kanade.tachiyomi.animeextension.all.missav
+
+import android.app.Application
+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.playlistutils.PlaylistUtils
+import eu.kanade.tachiyomi.lib.unpacker.Unpacker
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.util.asJsoup
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.Request
+import okhttp3.Response
+import rx.Observable
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+class MissAV : AnimeHttpSource(), ConfigurableAnimeSource {
+
+ override val name = "MissAV"
+
+ override val lang = "all"
+
+ override val baseUrl = "https://missav.com"
+
+ override val supportsLatest = true
+
+ override val client = network.cloudflareClient
+
+ override fun headersBuilder() = super.headersBuilder()
+ .add("Referer", "$baseUrl/")
+
+ private val playlistExtractor by lazy {
+ PlaylistUtils(client, headers)
+ }
+
+ private val preferences by lazy {
+ Injekt.get().getSharedPreferences("source_$id", 0x0000)
+ }
+
+ override fun popularAnimeRequest(page: Int) =
+ GET("$baseUrl/en/today-hot?page=$page", headers)
+
+ override fun popularAnimeParse(response: Response): AnimesPage {
+ val document = response.asJsoup()
+
+ val entries = document.select("div.thumbnail").map { element ->
+ SAnime.create().apply {
+ element.select("a.text-secondary").let {
+ setUrlWithoutDomain(it.attr("href"))
+ title = it.text()
+ }
+ thumbnail_url = element.selectFirst("img")?.attr("abs:data-src")
+ }
+ }
+
+ val hasNextPage = document.selectFirst("a[rel=next]") != null
+
+ return AnimesPage(entries, hasNextPage)
+ }
+
+ override fun latestUpdatesRequest(page: Int) =
+ GET("$baseUrl/en/new?page=$page", headers)
+
+ override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
+
+ override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
+ val url = baseUrl.toHttpUrl().newBuilder().apply {
+ val genre = filters.firstInstanceOrNull()?.selected
+ if (query.isNotEmpty()) {
+ addEncodedPathSegments("en/search")
+ addPathSegment(query.trim())
+ } else if (genre != null) {
+ addEncodedPathSegments(genre)
+ } else {
+ addEncodedPathSegments("en/new")
+ }
+ filters.firstInstanceOrNull()?.selected?.let {
+ addQueryParameter("sort", it)
+ }
+ addQueryParameter("page", page.toString())
+ }.build().toString()
+
+ return GET(url, headers)
+ }
+
+ override fun getFilterList() = getFilters()
+
+ override fun searchAnimeParse(response: Response) = popularAnimeParse(response)
+
+ override fun animeDetailsParse(response: Response): SAnime {
+ val document = response.asJsoup()
+
+ return SAnime.create().apply {
+ title = document.select("h1.text-base").text()
+ genre = document.select("div.text-secondary > a[href*=/genres/]").joinToString { it.text() }
+ description = document.select("div.mb-1").text()
+ author = document.select("div.text-secondary > a[href*=/makers/]").joinToString { it.text() }
+ artist = document.select("div.text-secondary > a[href*=/actresses/]").joinToString { it.text() }
+ status = SAnime.COMPLETED
+ thumbnail_url = document.select("video.player").attr("abs:data-poster")
+ }
+ }
+
+ override fun fetchEpisodeList(anime: SAnime): Observable> {
+ return Observable.just(
+ listOf(
+ SEpisode.create().apply {
+ url = anime.url
+ name = "Episode"
+ },
+ ),
+ )
+ }
+
+ override fun videoListParse(response: Response): List