diff --git a/src/ar/mangastorm/AndroidManifest.xml b/src/ar/mangastorm/AndroidManifest.xml
new file mode 100644
index 000000000..8072ee00d
--- /dev/null
+++ b/src/ar/mangastorm/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/src/ar/mangastorm/build.gradle b/src/ar/mangastorm/build.gradle
new file mode 100644
index 000000000..4bca18b48
--- /dev/null
+++ b/src/ar/mangastorm/build.gradle
@@ -0,0 +1,11 @@
+ext {
+ extName = 'MangaStorm'
+ extClass = '.MangaStorm'
+ extVersionCode = 1
+}
+
+apply from: "$rootDir/common.gradle"
+
+dependencies {
+ implementation(project(":lib:randomua"))
+}
diff --git a/src/ar/mangastorm/res/mipmap-hdpi/ic_launcher.png b/src/ar/mangastorm/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..e53f36188
Binary files /dev/null and b/src/ar/mangastorm/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/ar/mangastorm/res/mipmap-mdpi/ic_launcher.png b/src/ar/mangastorm/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..b40aa4858
Binary files /dev/null and b/src/ar/mangastorm/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/ar/mangastorm/res/mipmap-xhdpi/ic_launcher.png b/src/ar/mangastorm/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..f4a56bc00
Binary files /dev/null and b/src/ar/mangastorm/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/ar/mangastorm/res/mipmap-xxhdpi/ic_launcher.png b/src/ar/mangastorm/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..b24b899be
Binary files /dev/null and b/src/ar/mangastorm/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/ar/mangastorm/res/mipmap-xxxhdpi/ic_launcher.png b/src/ar/mangastorm/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..9204e5e4d
Binary files /dev/null and b/src/ar/mangastorm/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/ar/mangastorm/src/eu/kanade/tachiyomi/extension/ar/mangastorm/MangaStorm.kt b/src/ar/mangastorm/src/eu/kanade/tachiyomi/extension/ar/mangastorm/MangaStorm.kt
new file mode 100644
index 000000000..6348218de
--- /dev/null
+++ b/src/ar/mangastorm/src/eu/kanade/tachiyomi/extension/ar/mangastorm/MangaStorm.kt
@@ -0,0 +1,105 @@
+package eu.kanade.tachiyomi.extension.ar.mangastorm
+
+import eu.kanade.tachiyomi.lib.randomua.UserAgentType
+import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.model.FilterList
+import eu.kanade.tachiyomi.source.model.Page
+import eu.kanade.tachiyomi.source.model.SChapter
+import eu.kanade.tachiyomi.source.model.SManga
+import eu.kanade.tachiyomi.source.online.ParsedHttpSource
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.Request
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import java.lang.UnsupportedOperationException
+
+class MangaStorm : ParsedHttpSource() {
+
+ override val name = "MangaStorm"
+
+ override val lang = "ar"
+
+ override val baseUrl = "https://mangastorm.org"
+
+ override val supportsLatest = true
+
+ override val client = network.cloudflareClient.newBuilder()
+ .setRandomUserAgent(
+ UserAgentType.DESKTOP,
+ filterInclude = listOf("chrome"),
+ )
+ .build()
+
+ override fun headersBuilder() = super.headersBuilder()
+ .set("Referer", "$baseUrl/")
+
+ override fun popularMangaRequest(page: Int): Request {
+ return GET("$baseUrl/mangas?page=$page", headers)
+ }
+
+ override fun popularMangaSelector() = "div.row div.col"
+ override fun popularMangaNextPageSelector() = ".page-link[rel=next]"
+
+ override fun popularMangaFromElement(element: Element) = SManga.create().apply {
+ setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
+ title = element.select(".manga-ct-title").text()
+ thumbnail_url = element.selectFirst("img")?.imgAttr()
+ }
+
+ override fun latestUpdatesRequest(page: Int): Request {
+ return GET(baseUrl, headers)
+ }
+
+ override fun latestUpdatesSelector() = popularMangaSelector()
+ override fun latestUpdatesNextPageSelector() = null
+ override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ val url = "$baseUrl/mangas".toHttpUrl().newBuilder()
+ .addQueryParameter("page", page.toString())
+ .addQueryParameter("query", query.trim())
+ .build()
+
+ return GET(url, headers)
+ }
+
+ override fun searchMangaSelector() = popularMangaSelector()
+ override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
+ override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
+
+ override fun mangaDetailsParse(document: Document): SManga {
+ val root = document.selectFirst(".card-body .col-lg-9")!!
+
+ return SManga.create().apply {
+ title = document.select(".card-header").text()
+ thumbnail_url = document.selectFirst("img.card-img-right")?.imgAttr()
+ genre = root.select(".flex-wrap a").eachText().joinToString()
+ description = root.selectFirst(".card-text")?.text()
+ }
+ }
+
+ override fun chapterListSelector() = ".card-body a.btn-fixed-width"
+
+ override fun chapterFromElement(element: Element) = SChapter.create().apply {
+ setUrlWithoutDomain(element.absUrl("href"))
+ name = element.text()
+ }
+
+ override fun pageListParse(document: Document): List {
+ return document.select("div.text-center .img-fluid")
+ .mapIndexed { idx, img ->
+ Page(idx, "", img.imgAttr())
+ }
+ }
+
+ override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
+
+ private fun Element.imgAttr() = when {
+ hasAttr("data-cfsrc") -> attr("abs:data-cfsrc")
+ hasAttr("data-src") -> attr("abs:data-src")
+ hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
+ hasAttr("srcset") -> attr("abs:srcset").substringBefore(" ")
+ else -> attr("abs:src")
+ }
+}