diff --git a/src/pt/bruttal/build.gradle b/src/pt/bruttal/build.gradle
new file mode 100644
index 000000000..4cba5e11e
--- /dev/null
+++ b/src/pt/bruttal/build.gradle
@@ -0,0 +1,12 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+
+ext {
+    extName = 'Bruttal'
+    pkgNameSuffix = 'pt.bruttal'
+    extClass = '.Bruttal'
+    extVersionCode = 1
+    libVersion = '1.2'
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/pt/bruttal/res/mipmap-hdpi/ic_launcher.png b/src/pt/bruttal/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..7acf1167f
Binary files /dev/null and b/src/pt/bruttal/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/pt/bruttal/res/mipmap-mdpi/ic_launcher.png b/src/pt/bruttal/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..c31c1d044
Binary files /dev/null and b/src/pt/bruttal/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/pt/bruttal/res/mipmap-xhdpi/ic_launcher.png b/src/pt/bruttal/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..ca5a5cabe
Binary files /dev/null and b/src/pt/bruttal/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/pt/bruttal/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/bruttal/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..662be6849
Binary files /dev/null and b/src/pt/bruttal/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/pt/bruttal/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/bruttal/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..a6f89087e
Binary files /dev/null and b/src/pt/bruttal/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/pt/bruttal/res/web_hi_res_512.png b/src/pt/bruttal/res/web_hi_res_512.png
new file mode 100644
index 000000000..9f4f3e7cc
Binary files /dev/null and b/src/pt/bruttal/res/web_hi_res_512.png differ
diff --git a/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/Bruttal.kt b/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/Bruttal.kt
new file mode 100644
index 000000000..2b83d1da8
--- /dev/null
+++ b/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/Bruttal.kt
@@ -0,0 +1,188 @@
+package eu.kanade.tachiyomi.extension.pt.bruttal
+
+import com.github.salomonbrys.kotson.array
+import com.github.salomonbrys.kotson.get
+import com.github.salomonbrys.kotson.obj
+import com.github.salomonbrys.kotson.string
+import com.google.gson.JsonElement
+import com.google.gson.JsonObject
+import com.google.gson.JsonParser
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.source.model.FilterList
+import eu.kanade.tachiyomi.source.model.MangasPage
+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.HttpSource
+import okhttp3.Headers
+import okhttp3.Request
+import okhttp3.Response
+import rx.Observable
+
+class Bruttal : HttpSource() {
+
+    override val name = "Bruttal"
+
+    override val baseUrl = "https://originals.omelete.com.br"
+
+    override val lang = "pt-BR"
+
+    override val supportsLatest = false
+
+    override fun headersBuilder(): Headers.Builder = Headers.Builder()
+        .add("Referer", "$baseUrl/bruttal/")
+        .add("User-Agent", USER_AGENT)
+
+    override fun popularMangaRequest(page: Int): Request {
+        val newHeaders = headersBuilder()
+            .add("Accept", "application/json, text/plain, */*")
+            .build()
+
+        return GET("$baseUrl/bruttal/data/home.json", newHeaders)
+    }
+
+    override fun popularMangaParse(response: Response): MangasPage {
+        val json = response.asJson().obj
+
+        val titles = json["list"].array.map { jsonEl ->
+            popularMangaFromObject(jsonEl.obj)
+        }
+
+        return MangasPage(titles, false)
+    }
+
+    private fun popularMangaFromObject(obj: JsonObject): SManga = SManga.create().apply {
+        title = obj["title"].string
+        thumbnail_url = "$baseUrl/bruttal/" + obj["image_mobile"].string.removePrefix("./")
+        url = "/bruttal" + obj["url"].string
+    }
+
+    override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
+        return super.fetchSearchManga(page, query, filters)
+            .map { mp ->
+                val filteredTitles = mp.mangas.filter { it.title.contains(query, true) }
+                MangasPage(filteredTitles, mp.hasNextPage)
+            }
+    }
+
+    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = popularMangaRequest(page)
+
+    override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response)
+
+    // Workaround to allow "Open in browser" use the real URL.
+    override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
+        return client.newCall(mangaDetailsApiRequest(manga))
+            .asObservableSuccess()
+            .map { response ->
+                mangaDetailsParse(response).apply { initialized = true }
+            }
+    }
+
+    private fun mangaDetailsApiRequest(manga: SManga): Request {
+        val newHeaders = headersBuilder()
+            .add("Accept", "application/json, text/plain, */*")
+            .set("Referer", baseUrl + manga.url)
+            .build()
+
+        return GET("$baseUrl/bruttal/data/comicbooks.json", newHeaders)
+    }
+
+    override fun mangaDetailsParse(response: Response): SManga {
+        val json = response.asJson().array
+
+        val titleUrl = response.request().header("Referer")!!.substringAfter("/bruttal")
+        val titleObj = json.first { it.obj["url"].string == titleUrl }.obj
+        val soonText = titleObj["soon_text"].string
+
+        return SManga.create().apply {
+            title = titleObj["title"].string
+            thumbnail_url = "$baseUrl/bruttal/" + titleObj["image_mobile"].string.removePrefix("./")
+            description = titleObj["synopsis"].string +
+                (if (soonText.isEmpty()) "" else "\n\n$soonText")
+            artist = titleObj["illustrator"].string
+            author = titleObj["author"].string
+            genre = titleObj["keywords"].string.replace("; ", ", ")
+            status = SManga.ONGOING
+        }
+    }
+
+    // Chapters are available in the same url of the manga details.
+    override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga)
+
+    override fun chapterListParse(response: Response): List<SChapter> {
+        val json = response.asJson().array
+
+        val titleUrl = response.request().header("Referer")!!.substringAfter("/bruttal")
+        val title = json.first { it.obj["url"].string == titleUrl }.obj
+
+        return title["seasons"].array
+            .flatMap { it.obj["chapters"].array }
+            .map { jsonEl -> chapterFromObject(jsonEl.obj) }
+            .reversed()
+    }
+
+    private fun chapterFromObject(obj: JsonObject): SChapter = SChapter.create().apply {
+        name = obj["title"].string
+        chapter_number = obj["share_title"].string.removePrefix("CapĂ­tulo ").toFloatOrNull() ?: -1f
+        url = "/bruttal" + obj["url"].string
+    }
+
+    override fun pageListRequest(chapter: SChapter): Request {
+        val newHeaders = headersBuilder()
+            .add("Accept", "application/json, text/plain, */*")
+            .set("Referer", baseUrl + chapter.url)
+            .build()
+
+        return GET("$baseUrl/bruttal/data/comicbooks.json", newHeaders)
+    }
+
+    override fun pageListParse(response: Response): List<Page> {
+        val json = response.asJson().array
+
+        val chapterUrl = response.request().header("Referer")!!
+        val titleSlug = chapterUrl.substringAfter("bruttal/").substringBefore("/")
+        val season = chapterUrl.substringAfter("temporada-").substringBefore("/").toInt()
+        val chapter = chapterUrl.substringAfter("capitulo-")
+
+        val titleObj = json.first { it.obj["url"].string == "/$titleSlug" }.obj
+        val seasonObj = titleObj["seasons"].array[season - 1].obj
+        val chapterObj = seasonObj["chapters"].array.first {
+            it.obj["alias"].string.substringAfter("-") == chapter
+        }
+
+        return chapterObj["images"].array
+            .mapIndexed { i, jsonEl ->
+                val imageUrl = "$baseUrl/bruttal/" + jsonEl.obj["image"].string.removePrefix("./")
+                Page(i, chapterUrl, imageUrl)
+            }
+    }
+
+    override fun fetchImageUrl(page: Page): Observable<String> {
+        return Observable.just(page.imageUrl!!)
+    }
+
+    override fun imageUrlParse(response: Response): String = ""
+
+    override fun imageRequest(page: Page): Request {
+        val newHeaders = headersBuilder()
+            .add("Accept", "image/avif,image/webp,image/apng,image/*,*/*;q=0.8")
+            .set("Referer", page.url)
+            .build()
+
+        return GET(page.imageUrl!!, newHeaders)
+    }
+
+    override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException("Not used")
+
+    override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not used")
+
+    private fun Response.asJson(): JsonElement = JSON_PARSER.parse(body()!!.string())
+
+    companion object {
+        private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
+            "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
+
+        private val JSON_PARSER by lazy { JsonParser() }
+    }
+}