diff --git a/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt b/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt
deleted file mode 100644
index 0163cc160..000000000
--- a/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package eu.kanade.tachiyomi.extension.pt.yugenmangas
-
-import androidx.preference.PreferenceScreen
-import eu.kanade.tachiyomi.lib.randomua.UserAgentType
-import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
-import eu.kanade.tachiyomi.multisrc.madara.Madara
-import eu.kanade.tachiyomi.network.interceptor.rateLimit
-import eu.kanade.tachiyomi.source.model.SChapter
-import okhttp3.OkHttpClient
-import org.jsoup.nodes.Element
-import java.text.SimpleDateFormat
-import java.util.Locale
-import java.util.concurrent.TimeUnit
-
-class YugenMangas : Madara(
- "YugenMangas",
- "https://yugenmangas.com.br",
- "pt-BR",
- SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
-) {
-
- override val client: OkHttpClient = network.cloudflareClient.newBuilder()
- .setRandomUserAgent(
- UserAgentType.DESKTOP,
- )
- .connectTimeout(10, TimeUnit.SECONDS)
- .readTimeout(30, TimeUnit.SECONDS)
- .rateLimit(1, 3, TimeUnit.SECONDS)
- .build()
-
- override fun headersBuilder() = super.headersBuilder()
- .add("Origin", baseUrl)
-
- override val useNewChapterEndpoint: Boolean = true
-
- override val mangaSubString = "series"
-
- override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
- name = element.selectFirst("p.chapter-manhwa-title")!!.text()
- date_upload = parseChapterDate(element.selectFirst("span.chapter-release-date i")?.text())
-
- val chapterUrl = element.selectFirst("a")!!.attr("abs:href")
- setUrlWithoutDomain(
- chapterUrl.substringBefore("?style=paged") +
- if (!chapterUrl.endsWith(chapterUrlSuffix)) chapterUrlSuffix else "",
- )
- }
-
- override fun setupPreferenceScreen(screen: PreferenceScreen) { }
-}
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt
index eaa78e652..fac8ee845 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt
@@ -507,7 +507,6 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("YaoiScan", "https://yaoiscan.com", "en", isNsfw = true),
SingleLang("YaoiToon", "https://yaoitoon.com", "en", isNsfw = true),
SingleLang("YonaBar", "https://yonabar.com", "ar", isNsfw = true, overrideVersionCode = 2),
- SingleLang("YugenMangas", "https://yugenmangas.com.br", "pt-BR", overrideVersionCode = 2),
SingleLang("Yuri Verso", "https://yuri.live", "pt-BR", overrideVersionCode = 3),
SingleLang("Zandy no Fansub", "https://zandynofansub.aishiteru.org", "en"),
SingleLang("Zero Scan", "https://zeroscan.com.br", "pt-BR", isNsfw = true),
diff --git a/src/pt/yugenmangas/AndroidManifest.xml b/src/pt/yugenmangas/AndroidManifest.xml
new file mode 100644
index 000000000..8072ee00d
--- /dev/null
+++ b/src/pt/yugenmangas/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/src/pt/yugenmangas/build.gradle b/src/pt/yugenmangas/build.gradle
new file mode 100644
index 000000000..1d4b531b0
--- /dev/null
+++ b/src/pt/yugenmangas/build.gradle
@@ -0,0 +1,12 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlinx-serialization'
+
+ext {
+ extName = 'Yugen Mangás'
+ pkgNameSuffix = 'pt.yugenmangas'
+ extClass = '.YugenMangas'
+ extVersionCode = 34
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-hdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/yugenmangas/res/mipmap-hdpi/ic_launcher.png
rename to src/pt/yugenmangas/res/mipmap-hdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-mdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/yugenmangas/res/mipmap-mdpi/ic_launcher.png
rename to src/pt/yugenmangas/res/mipmap-mdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-xhdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/yugenmangas/res/mipmap-xhdpi/ic_launcher.png
rename to src/pt/yugenmangas/res/mipmap-xhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/yugenmangas/res/mipmap-xxhdpi/ic_launcher.png
rename to src/pt/yugenmangas/res/mipmap-xxhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/yugenmangas/res/mipmap-xxxhdpi/ic_launcher.png
rename to src/pt/yugenmangas/res/mipmap-xxxhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/yugenmangas/res/web_hi_res_512.png b/src/pt/yugenmangas/res/web_hi_res_512.png
similarity index 100%
rename from multisrc/overrides/madara/yugenmangas/res/web_hi_res_512.png
rename to src/pt/yugenmangas/res/web_hi_res_512.png
diff --git a/src/pt/yugenmangas/src/eu/kanade/tachiyomi/extension/pt/yugenmangas/YugenMangas.kt b/src/pt/yugenmangas/src/eu/kanade/tachiyomi/extension/pt/yugenmangas/YugenMangas.kt
new file mode 100644
index 000000000..80d66f43b
--- /dev/null
+++ b/src/pt/yugenmangas/src/eu/kanade/tachiyomi/extension/pt/yugenmangas/YugenMangas.kt
@@ -0,0 +1,159 @@
+package eu.kanade.tachiyomi.extension.pt.yugenmangas
+
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.interceptor.rateLimit
+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.ParsedHttpSource
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import okhttp3.Headers
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.Response
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import uy.kohesive.injekt.injectLazy
+import java.text.SimpleDateFormat
+import java.util.Locale
+import java.util.concurrent.TimeUnit
+
+/**
+ * Changed the name from "YugenMangas" to "Yugen Mangás" when
+ * the source was updated to handle their CMS changes, so no
+ * `versionId` change is needed as the ID should be different to
+ * force users to migrate.
+ */
+class YugenMangas : ParsedHttpSource() {
+
+ override val name = "Yugen Mangás"
+
+ override val baseUrl = "https://yugenmangas.net.br"
+
+ override val lang = "pt-BR"
+
+ override val supportsLatest = true
+
+ override val client: OkHttpClient = network.cloudflareClient.newBuilder()
+ .rateLimit(1, 2, TimeUnit.SECONDS)
+ .build()
+
+ private val json: Json by injectLazy()
+
+ override fun headersBuilder(): Headers.Builder = Headers.Builder()
+ .add("Referer", "$baseUrl/")
+
+ override fun popularMangaRequest(page: Int): Request = GET(baseUrl, headers)
+
+ override fun popularMangaSelector(): String = "div.container-popular div.swiper-wrapper a"
+
+ override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
+ title = element.selectFirst("h1")!!.text()
+ thumbnail_url = element.selectFirst("img")!!.absUrl("src")
+ url = element.attr("href")
+ }
+
+ override fun popularMangaNextPageSelector(): String? = null
+
+ override fun latestUpdatesRequest(page: Int): Request {
+ return GET("$baseUrl/updates/?page=$page", headers)
+ }
+
+ override fun latestUpdatesSelector() = "div.container-update-series div.card-series-updates"
+
+ override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply {
+ title = element.selectFirst("a.title-serie h1")!!.text()
+ thumbnail_url = element.selectFirst("img")!!.absUrl("src")
+ url = element.selectFirst("a")!!.attr("href")
+ }
+
+ override fun latestUpdatesNextPageSelector() = "div.pagination a:contains(Próxima)"
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ val url = "$baseUrl/api/series/list".toHttpUrl().newBuilder()
+ .addQueryParameter("query", query)
+ .build()
+
+ return GET(url, headers)
+ }
+
+ override fun searchMangaParse(response: Response): MangasPage {
+ val result = json.decodeFromString>(response.body.string())
+ val matches = result.map {
+ SManga.create().apply {
+ title = it.name
+ url = "/series/${it.slug}"
+ }
+ }
+
+ return MangasPage(matches, hasNextPage = false)
+ }
+
+ override fun searchMangaSelector() = throw UnsupportedOperationException("Not used")
+
+ override fun searchMangaFromElement(element: Element) = throw UnsupportedOperationException("Not used")
+
+ override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not used")
+
+ override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
+ val infoElement = document.selectFirst("div.main div.resume > div.sinopse")!!
+
+ title = infoElement.selectFirst("div.title-name h1")!!.text()
+ author = infoElement.selectFirst("div.author")!!.text()
+ genre = infoElement.select("div.genero span").joinToString { it.text() }
+ status = infoElement.selectFirst("div.lancamento p")!!.text().toStatus()
+ description = infoElement.select("div.sinopse > p").text()
+ thumbnail_url = document.selectFirst("div.content div.side div.top-side img")!!.absUrl("src")
+ }
+
+ override fun chapterListSelector() = "#listadecapitulos div.chapter a"
+
+ override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
+ name = element.selectFirst("span.chapter-title")!!.text()
+ scanlator = element.selectFirst("div.end-chapter span")?.text()
+ date_upload = element.selectFirst("span.chapter-lancado")!!.text().toDate()
+ url = element.attr("href")
+ }
+
+ override fun pageListParse(document: Document): List {
+ return document.select("div.chapter-content img.chapter-image")
+ .mapIndexed { index, element ->
+ Page(index, document.location(), element.absUrl("src"))
+ }
+ }
+
+ override fun imageUrlParse(document: Document) = ""
+
+ override fun imageRequest(page: Page): Request {
+ val newHeaders = headersBuilder()
+ .set("Referer", page.url)
+ .build()
+
+ return GET(page.imageUrl!!, newHeaders)
+ }
+
+ @Serializable
+ private data class SearchResultDto(val name: String, val slug: String)
+
+ private fun String.toDate(): Long {
+ return runCatching { DATE_FORMATTER.parse(trim())?.time }
+ .getOrNull() ?: 0L
+ }
+
+ private fun String.toStatus() = when (this) {
+ "ongoing" -> SManga.ONGOING
+ "completed", "finished" -> SManga.COMPLETED
+ else -> SManga.UNKNOWN
+ }
+
+ companion object {
+ private val DATE_FORMATTER by lazy {
+ SimpleDateFormat("dd.MM.yyyy", Locale("pt", "BR"))
+ }
+ }
+}