diff --git a/.run/BakaMangaGenerator.run.xml b/.run/BakaMangaGenerator.run.xml
new file mode 100644
index 000000000..b0cc06ee6
--- /dev/null
+++ b/.run/BakaMangaGenerator.run.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..76c8c3f52
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..f424825d8
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..f9cbb9e40
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..0e573f61c
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..fcb0be641
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhuamanganet/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhuamanganet/res/web_hi_res_512.png b/multisrc/overrides/bakamanga/manhuamanganet/res/web_hi_res_512.png
new file mode 100644
index 000000000..e55492a09
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhuamanganet/res/web_hi_res_512.png differ
diff --git a/multisrc/overrides/bakamanga/manhuamanganet/src/ManhuaMangaNet.kt b/multisrc/overrides/bakamanga/manhuamanganet/src/ManhuaMangaNet.kt
new file mode 100644
index 000000000..3d484c802
--- /dev/null
+++ b/multisrc/overrides/bakamanga/manhuamanganet/src/ManhuaMangaNet.kt
@@ -0,0 +1,48 @@
+package eu.kanade.tachiyomi.extension.en.manhuamanganet
+
+import eu.kanade.tachiyomi.multisrc.bakamanga.BakaManga
+
+class ManhuaMangaNet : BakaManga(
+ "ManhuaManga.net",
+ "https://manhuamanga.net",
+ "en"
+) {
+ override fun getGenreList() = arrayOf(
+ Pair("All", ""),
+ Pair("Action", "action"),
+ Pair("Adventure", "adventure"),
+ Pair("Based on a Novel", "based-on-a-novel"),
+ Pair("Comedy", "comedy"),
+ Pair("Comic", "comic"),
+ Pair("Cooking", "cooking"),
+ Pair("Drama", "drama"),
+ Pair("Ecchi", "ecchi"),
+ Pair("Fantasy", "fantasy"),
+ Pair("Harem", "harem"),
+ Pair("Historical", "historical"),
+ Pair("Horror", "horror"),
+ Pair("Isekai", "isekai"),
+ Pair("Josei", "josei"),
+ Pair("Magic", "magic"),
+ Pair("Manhua", "manhua"),
+ Pair("Manhwa", "manhwa"),
+ Pair("Martial Arts", "martial-arts"),
+ Pair("Mecha", "mecha"),
+ Pair("Medical", "medical"),
+ Pair("Mystery", "mystery"),
+ Pair("Psychological", "psychological"),
+ Pair("Reincarnation", "reincarnation"),
+ Pair("Romance", "romance"),
+ Pair("RPG", "rpg"),
+ Pair("School Life", "school-life"),
+ Pair("Sci-fi", "sci-fi"),
+ Pair("Seinen", "seinen"),
+ Pair("Shoujo", "shoujo"),
+ Pair("Shounen", "shounen"),
+ Pair("Slice of Life", "slice-of-life"),
+ Pair("Supernatural", "supernatural"),
+ Pair("Tragedy", "tragedy"),
+ Pair("Webtoon", "webtoon"),
+ Pair("Zombie", "zombie"),
+ )
+}
diff --git a/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..7f9090fc4
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..35510c59a
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..227a32f02
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..fe01d68cd
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..e1944a0b5
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwamanganet/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwamanganet/res/web_hi_res_512.png b/multisrc/overrides/bakamanga/manhwamanganet/res/web_hi_res_512.png
new file mode 100644
index 000000000..a2a5156da
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwamanganet/res/web_hi_res_512.png differ
diff --git a/multisrc/overrides/bakamanga/manhwamanganet/src/ManhwaMangaNet.kt b/multisrc/overrides/bakamanga/manhwamanganet/src/ManhwaMangaNet.kt
new file mode 100644
index 000000000..1ab29561b
--- /dev/null
+++ b/multisrc/overrides/bakamanga/manhwamanganet/src/ManhwaMangaNet.kt
@@ -0,0 +1,55 @@
+package eu.kanade.tachiyomi.extension.en.manhwamanganet
+
+import eu.kanade.tachiyomi.multisrc.bakamanga.BakaManga
+
+class ManhwaMangaNet : BakaManga(
+ "ManhwaManga.net",
+ "https://manhwamanga.net",
+ "en"
+) {
+ override fun getGenreList() = arrayOf(
+ Pair("All", ""),
+ Pair("Action", "action"),
+ Pair("Adult", "adult"),
+ Pair("Adventure", "adventure"),
+ Pair("BL", "bl"),
+ Pair("Comedy", "comedy"),
+ Pair("Comics", "comics"),
+ Pair("Doujinshi", "doujinshi"),
+ Pair("Drama", "drama"),
+ Pair("Ecchi", "ecchi"),
+ Pair("Fantasy", "fantasy"),
+ Pair("Gender Bender", "gender-bender"),
+ Pair("GL", "gl"),
+ Pair("Harem", "harem"),
+ Pair("Hentai", "hentai"),
+ Pair("Historical", "historical"),
+ Pair("Horror", "horror"),
+ Pair("Isekai", "isekai"),
+ Pair("Josei", "josei"),
+ Pair("Manhwa", "manhwa"),
+ Pair("Martial Arts", "martial-arts"),
+ Pair("Mature", "mature"),
+ Pair("Mecha", "mecha"),
+ Pair("Mystery", "mystery"),
+ Pair("NTR", "ntr"),
+ Pair("Psychological", "psychological"),
+ Pair("Raw", "raw"),
+ Pair("Romance", "romance"),
+ Pair("School Life", "school-life"),
+ Pair("Sci-fi", "sci-fi"),
+ Pair("Seinen", "seinen"),
+ Pair("Shoujo", "shoujo"),
+ Pair("Shounen", "shounen"),
+ Pair("Slice of Life", "slice-of-life"),
+ Pair("Smut", "smut"),
+ Pair("Sports", "sports"),
+ Pair("Supernatural", "supernatural"),
+ Pair("Thriller", "thriller"),
+ Pair("Tragedy", "tragedy"),
+ Pair("Webtoon", "webtoon"),
+ Pair("Webtoons", "webtoons"),
+ Pair("Yaoi", "yaoi"),
+ Pair("Yuri", "yuri"),
+ )
+}
diff --git a/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..32ea9d10c
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..a1e4fcd71
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..b6d29fc5b
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..3e545a58d
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..1c53e9207
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwaxxl/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/manhwaxxl/res/web_hi_res_512.png b/multisrc/overrides/bakamanga/manhwaxxl/res/web_hi_res_512.png
new file mode 100644
index 000000000..ba821393b
Binary files /dev/null and b/multisrc/overrides/bakamanga/manhwaxxl/res/web_hi_res_512.png differ
diff --git a/multisrc/overrides/bakamanga/manhwaxxl/src/ManhwaXXL.kt b/multisrc/overrides/bakamanga/manhwaxxl/src/ManhwaXXL.kt
new file mode 100644
index 000000000..07c2070ef
--- /dev/null
+++ b/multisrc/overrides/bakamanga/manhwaxxl/src/ManhwaXXL.kt
@@ -0,0 +1,68 @@
+package eu.kanade.tachiyomi.extension.en.manhwaxxl
+
+import eu.kanade.tachiyomi.multisrc.bakamanga.BakaManga
+
+class ManhwaXXL : BakaManga(
+ "Manhwa XXL",
+ "https://manhwaxxl.com",
+ "en"
+) {
+ override fun getGenreList() = arrayOf(
+ Pair("All", ""),
+ Pair("Action", "action"),
+ Pair("Adaptation", "adaptation"),
+ Pair("Adult", "adult"),
+ Pair("Adventure", "adventure"),
+ Pair("BL", "bl"),
+ Pair("Comedy", "comedy"),
+ Pair("Cooking", "cooking"),
+ Pair("Demons", "demons"),
+ Pair("Drama", "drama"),
+ Pair("Ecchi", "ecchi"),
+ Pair("Fantasy", "fantasy"),
+ Pair("Full color", "full-color"),
+ Pair("Game", "game"),
+ Pair("Gender Bender", "gender-bender"),
+ Pair("GL", "gl"),
+ Pair("Harem", "harem"),
+ Pair("Historical", "historical"),
+ Pair("Horror", "horror"),
+ Pair("Isekai", "isekai"),
+ Pair("Josei", "josei"),
+ Pair("Live action", "live-action"),
+ Pair("Love & Romance", "love-romance"),
+ Pair("Magic", "magic"),
+ Pair("Manga", "manga"),
+ Pair("Manhua", "manhua"),
+ Pair("Manhwa", "manhwa"),
+ Pair("Martial Arts", "martial-arts"),
+ Pair("Mature", "mature"),
+ Pair("Mecha", "mecha"),
+ Pair("Mystery", "mystery"),
+ Pair("Omegaverse", "omegaverse"),
+ Pair("Psychological", "psychological"),
+ Pair("Raw", "raw"),
+ Pair("Reincarnation", "reincarnation"),
+ Pair("Romance", "romance"),
+ Pair("RPG", "rpg"),
+ Pair("School Life", "school-life"),
+ Pair("Sci-fi", "sci-fi"),
+ Pair("Seinen", "seinen"),
+ Pair("Shoujo", "shoujo"),
+ Pair("Shoujo Ai", "shoujo-ai"),
+ Pair("Shounen", "shounen"),
+ Pair("Slice of Life", "slice-of-life"),
+ Pair("Smut", "smut"),
+ Pair("Sports", "sports"),
+ Pair("Supernatural", "supernatural"),
+ Pair("Thriller", "thriller"),
+ Pair("Tragedy", "tragedy"),
+ Pair("Vampire", "vampire"),
+ Pair("Vanilla", "vanilla"),
+ Pair("Webtoon", "webtoon"),
+ Pair("Webtoons", "webtoons"),
+ Pair("Yaoi", "yaoi"),
+ Pair("Yuri", "yuri"),
+ Pair("Zombie", "zombie"),
+ )
+}
diff --git a/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..45ba914a3
Binary files /dev/null and b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..8dcf49908
Binary files /dev/null and b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..d2ee0b29a
Binary files /dev/null and b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..979b03da4
Binary files /dev/null and b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..f97c41ccf
Binary files /dev/null and b/multisrc/overrides/bakamanga/mwmanhwa/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/bakamanga/mwmanhwa/res/web_hi_res_512.png b/multisrc/overrides/bakamanga/mwmanhwa/res/web_hi_res_512.png
new file mode 100644
index 000000000..0bedb66f0
Binary files /dev/null and b/multisrc/overrides/bakamanga/mwmanhwa/res/web_hi_res_512.png differ
diff --git a/multisrc/overrides/bakamanga/mwmanhwa/src/MWManhwa.kt b/multisrc/overrides/bakamanga/mwmanhwa/src/MWManhwa.kt
new file mode 100644
index 000000000..0ec30735e
--- /dev/null
+++ b/multisrc/overrides/bakamanga/mwmanhwa/src/MWManhwa.kt
@@ -0,0 +1,27 @@
+package eu.kanade.tachiyomi.extension.all.mwmanhwa
+
+import eu.kanade.tachiyomi.multisrc.bakamanga.BakaManga
+
+class MWManhwa : BakaManga(
+ "MWManhwa",
+ "https://mwmanhwa.net",
+ "all"
+) {
+ override fun getGenreList() = arrayOf(
+ Pair("All", ""),
+ Pair("Action", "action"),
+ Pair("Comedy", "comedy"),
+ Pair("Drama", "drama"),
+ Pair("Ecchi", "ecchi"),
+ Pair("Fantasy", "fantasy"),
+ Pair("Gender Bender", "gender-bender"),
+ Pair("Harem", "harem"),
+ Pair("Mature", "mature"),
+ Pair("Psychological", "psychological"),
+ Pair("Raw", "adult"),
+ Pair("Romance", "romance"),
+ Pair("Seinen", "seinen"),
+ Pair("Slice of Life", "slice-of-life"),
+ Pair("Supernatural", "supernatural"),
+ )
+}
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaManga.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaManga.kt
new file mode 100644
index 000000000..7c37e311c
--- /dev/null
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaManga.kt
@@ -0,0 +1,176 @@
+package eu.kanade.tachiyomi.multisrc.bakamanga
+
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.model.Filter
+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 okhttp3.Response
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import java.util.Calendar
+
+abstract class BakaManga(
+ override val name: String,
+ override val baseUrl: String,
+ override val lang: String
+) : ParsedHttpSource() {
+ override val supportsLatest = true
+
+ // Popular
+ override fun popularMangaRequest(page: Int): Request =
+ GET("$baseUrl/most-views/page/$page", headers)
+
+ override fun popularMangaSelector(): String =
+ ".li_truyen"
+
+ override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
+ setUrlWithoutDomain(element.selectFirst("a").absUrl("href"))
+ title = element.selectFirst(".name").text()
+ thumbnail_url = element.selectFirst("img").absUrl("src")
+ }
+
+ override fun popularMangaNextPageSelector(): String? =
+ ".page_redirect > a:last-child:not(.active)"
+
+ // Search
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ return if (query.isNotEmpty()) {
+ val url = "$baseUrl/page/$page".toHttpUrl().newBuilder()
+ .addQueryParameter("s", query)
+ GET(url.toString(), headers)
+ } else {
+ val filterList = if (filters.isEmpty()) getFilterList() else filters
+ val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
+ val url = "$baseUrl/category/${genreFilter.toUriPart()}/page/$page"
+ GET(url.toString(), headers)
+ }
+ }
+
+ override fun searchMangaSelector(): String =
+ popularMangaSelector()
+
+ override fun searchMangaFromElement(element: Element): SManga =
+ popularMangaFromElement(element)
+
+ override fun searchMangaNextPageSelector(): String? =
+ popularMangaNextPageSelector()
+
+ // Latest
+ override fun latestUpdatesRequest(page: Int): Request =
+ GET("$baseUrl/latest-updates/page/$page", headers)
+
+ override fun latestUpdatesSelector(): String =
+ popularMangaSelector()
+
+ override fun latestUpdatesFromElement(element: Element): SManga =
+ popularMangaFromElement(element)
+
+ override fun latestUpdatesNextPageSelector(): String? =
+ popularMangaNextPageSelector()
+
+ // Details
+ override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
+ val info = document.selectFirst(".box_info")
+ title = info.selectFirst("h1").text()
+ artist = info.select(".info-item:contains(Artist:) > a").joinToString { it.text() }
+
+ val descElements = info.select(".story-detail-info:matchText")
+ description = when {
+ descElements.size > 2 -> {
+ descElements.removeFirst() // "Summary:"
+ descElements.removeLast() // "-From example.com"
+ descElements.joinToString("\n") { it.text() }
+ }
+ else -> ""
+ }
+
+ val altTitles = info.selectFirst(".info-item:contains(Alternate Title:)")
+ ?.text()
+ ?.removePrefix("Alternate Title:")
+ ?.trim()
+
+ if (altTitles != null && altTitles.isNotEmpty()) {
+ description += "\n\nAlt title(s): $altTitles"
+ }
+
+ genre = info.select(".post-categories > li > a").joinToString { it.text() }
+ status = info.selectFirst(".info-item:contains(Status:)").text()
+ .removePrefix("Status:")
+ .trim()
+ .toStatus()
+
+ thumbnail_url = info.selectFirst(".box_info img").absUrl("src")
+ }
+
+ // Chapters
+ override fun chapterListParse(response: Response): List =
+ super.chapterListParse(response).reversed()
+
+ override fun chapterListSelector(): String =
+ ".list-chapters > .list-chapters > .box_list > .chapter-item"
+
+ override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
+ setUrlWithoutDomain(element.selectFirst("a").absUrl("href"))
+ name = element.selectFirst(".chap_name").text()
+ chapter_number = name
+ .substringAfter(' ')
+ .substringBefore(' ')
+ .toFloatOrNull() ?: -1f
+
+ date_upload = parseRelativeDate(element.selectFirst(".chap_update").text())
+ }
+
+ private fun parseRelativeDate(date: String): Long {
+ val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
+ val cal = Calendar.getInstance()
+
+ return when {
+ date.contains("year") -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
+ date.contains("month") -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
+ date.contains("week") -> cal.apply { add(Calendar.WEEK_OF_YEAR, -number) }.timeInMillis
+ date.contains("day") -> cal.apply { add(Calendar.DAY_OF_YEAR, -number) }.timeInMillis
+ date.contains("hour") -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
+ date.contains("minute") -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
+ date.contains("second") -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
+ else -> 0
+ }
+ }
+
+ // Pages
+ override fun pageListParse(document: Document): List {
+ return document.select("noscript > img").mapIndexed { i, img ->
+ Page(i, document.location(), img.absUrl("src"))
+ }
+ }
+
+ override fun imageUrlParse(document: Document): String =
+ ""
+
+ // Filter
+ override fun getFilterList() = FilterList(
+ Filter.Header("NOTE: Ignored if using text search!"),
+ Filter.Separator(),
+ GenreFilter(getGenreList())
+ )
+
+ class GenreFilter(vals: Array>) : UriPartFilter("Category", vals)
+
+ abstract fun getGenreList(): Array>
+
+ open class UriPartFilter(displayName: String, private val vals: Array>) :
+ Filter.Select(displayName, vals.map { it.first }.toTypedArray()) {
+ fun toUriPart() = vals[state].second
+ }
+
+ // Other
+ private fun String.toStatus() = when (this) {
+ "Ongoing" -> SManga.ONGOING
+ "Completed" -> SManga.COMPLETED
+ else -> SManga.UNKNOWN
+ }
+}
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaMangaGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaMangaGenerator.kt
new file mode 100644
index 000000000..ff5325d52
--- /dev/null
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaMangaGenerator.kt
@@ -0,0 +1,24 @@
+package eu.kanade.tachiyomi.multisrc.bakamanga
+
+import generator.ThemeSourceData.SingleLang
+import generator.ThemeSourceGenerator
+
+class BakaMangaGenerator : ThemeSourceGenerator {
+ override val themePkg = "bakamanga"
+
+ override val themeClass = "BakaManga"
+
+ override val baseVersionCode = 1
+
+ override val sources = listOf(
+ SingleLang("ManhuaManga.net", "https://manhuamanga.net", "en", className = "ManhuaMangaNet", overrideVersionCode = 2),
+ SingleLang("ManhwaManga.net", "https://manhwamanga.net", "en", isNsfw = true, className = "ManhwaMangaNet", overrideVersionCode = 7),
+ SingleLang("MWManhwa", "https://mwmanhwa.net", "all", isNsfw = true),
+ SingleLang("Manhwa XXL", "https://manhwaxxl.com", "en", isNsfw = true),
+ )
+
+ companion object {
+ @JvmStatic
+ fun main(args: Array) = BakaMangaGenerator().createAll()
+ }
+}
diff --git a/src/en/manhuamanga/AndroidManifest.xml b/src/en/manhuamanga/AndroidManifest.xml
deleted file mode 100644
index 30deb7f79..000000000
--- a/src/en/manhuamanga/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/src/en/manhuamanga/build.gradle b/src/en/manhuamanga/build.gradle
deleted file mode 100644
index 69f678745..000000000
--- a/src/en/manhuamanga/build.gradle
+++ /dev/null
@@ -1,12 +0,0 @@
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-
-ext {
- extName = 'ManhuaManga.net'
- pkgNameSuffix = 'en.manhuamanga'
- extClass = '.ManhuaManga'
- extVersionCode = 2
- isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/en/manhuamanga/res/mipmap-hdpi/ic_launcher.png b/src/en/manhuamanga/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 580694e1b..000000000
Binary files a/src/en/manhuamanga/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhuamanga/res/mipmap-mdpi/ic_launcher.png b/src/en/manhuamanga/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 8efd2f57f..000000000
Binary files a/src/en/manhuamanga/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhuamanga/res/mipmap-xhdpi/ic_launcher.png b/src/en/manhuamanga/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index a600bdf93..000000000
Binary files a/src/en/manhuamanga/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhuamanga/res/mipmap-xxhdpi/ic_launcher.png b/src/en/manhuamanga/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index dd11d3f4e..000000000
Binary files a/src/en/manhuamanga/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhuamanga/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/manhuamanga/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index a24fb6f44..000000000
Binary files a/src/en/manhuamanga/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhuamanga/res/web_hi_res_512.png b/src/en/manhuamanga/res/web_hi_res_512.png
deleted file mode 100644
index 2ef804be2..000000000
Binary files a/src/en/manhuamanga/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/manhuamanga/src/eu/kanade/tachiyomi/extension/en/manhuamanga/ManhuaManga.kt b/src/en/manhuamanga/src/eu/kanade/tachiyomi/extension/en/manhuamanga/ManhuaManga.kt
deleted file mode 100644
index c5e279b02..000000000
--- a/src/en/manhuamanga/src/eu/kanade/tachiyomi/extension/en/manhuamanga/ManhuaManga.kt
+++ /dev/null
@@ -1,161 +0,0 @@
-package eu.kanade.tachiyomi.extension.en.manhuamanga
-
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.source.model.Filter
-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 eu.kanade.tachiyomi.util.asJsoup
-import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
-import okhttp3.Request
-import okhttp3.RequestBody.Companion.toRequestBody
-import okhttp3.Response
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-
-class ManhuaManga : ParsedHttpSource() {
- override val name = "ManhuaManga.net"
- override val baseUrl = "https://manhuamanga.net"
- override val lang = "en"
- override val supportsLatest = true
-
- override fun popularMangaSelector() = ".home-truyendecu"
- override fun latestUpdatesSelector() = popularMangaSelector()
- override fun searchMangaSelector() = popularMangaSelector()
- override fun chapterListSelector() = "#list-chapter > div.row > div > ul > li:nth-child(n)"
-
- override fun popularMangaNextPageSelector() = "li.active+li a[data-page]"
- override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
- override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
-
- override fun popularMangaRequest(page: Int): Request {
- return GET("$baseUrl/most-views/page/$page", headers)
- }
-
- override fun latestUpdatesRequest(page: Int): Request {
- return GET("$baseUrl/latest-updates/page/$page", headers)
- }
-
- override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
- return if (query.isNotBlank()) {
- GET("$baseUrl/?s=$query", headers)
- } else {
- val url = "$baseUrl/category/".toHttpUrlOrNull()!!.newBuilder()
- filters.forEach { filter ->
- when (filter) {
-
- is GenreFilter -> url.addPathSegment(filter.toUriPart())
- }
- }
- url.addPathSegment("page")
- url.addPathSegment("$page")
- GET(url.toString(), headers)
- }
- }
- override fun popularMangaFromElement(element: Element): SManga {
- val manga = SManga.create()
- manga.setUrlWithoutDomain(element.select("a").attr("href"))
- manga.title = element.select("a").attr("title")
- manga.thumbnail_url = element.select("a img").attr("src")
-
- return manga
- }
- override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
- override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
-
- protected fun getXhrChapters(mangaId: String): Document {
- val xhrHeaders = headersBuilder().add("Content-Type: application/x-www-form-urlencoded; charset=UTF-8")
- .build()
- val body = "action=tw_ajax&type=list_chap&id=$mangaId".toRequestBody(null)
- return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, body)).execute().asJsoup()
- }
-
- override fun chapterListParse(response: Response): List {
- val document = response.asJsoup()
- val dataIdSelector = "input[id^=id_post]"
-
- return getXhrChapters(document.select(dataIdSelector).attr("value")).select("option").map { chapterFromElement(it) }.reversed()
- }
-
- override fun chapterFromElement(element: Element): SChapter {
- val chapter = SChapter.create()
- element.let { urlElement ->
- chapter.setUrlWithoutDomain(urlElement.attr("value"))
- chapter.name = urlElement.text()
- }
- chapter.date_upload = 0
-
- return chapter
- }
-
- override fun mangaDetailsParse(document: Document) = SManga.create().apply {
- title = document.select("h3.title").text()
- description = document.select("div.desc-text > p").text()
- thumbnail_url = document.select("div.books > div > img").attr("src")
- author = document.select("div.info > div:nth-child(1) > a").attr("title")
- genre = document.select("div.info > div:nth-child(2) > a").joinToString { it.text() }
- status = document.select("div.info > div:nth-child(3) > span").text().let {
- when {
- it.contains("Ongoing") -> SManga.ONGOING
- it.contains("Completed") -> SManga.COMPLETED
- else -> SManga.UNKNOWN
- }
- }
- }
-
- override fun pageListParse(document: Document): List = mutableListOf().apply {
- document.select("p img").forEachIndexed { index, element ->
- add(Page(index, "", element.attr("src")))
- }
- }
-
- override fun imageUrlRequest(page: Page) = throw Exception("Not used")
- override fun imageUrlParse(document: Document) = throw Exception("Not used")
-
- override fun getFilterList() = FilterList(
- Filter.Header("NOTE: Ignored if using text search!"),
- Filter.Separator(),
- GenreFilter(getGenreList())
- )
- class GenreFilter(vals: Array>) : UriPartFilter("Category", vals)
-
- private fun getGenreList() = arrayOf(
- Pair("All", ""),
- Pair("Action", "action"),
- Pair("Adventure", "adventure"),
- Pair("Comedy", "comedy"),
- Pair("Comic", "comic"),
- Pair("Drama", "drama"),
- Pair("Ecchi", "ecchi"),
- Pair("Fantasy", "fantasy"),
- Pair("Harem", "harem"),
- Pair("Historical", "historical"),
- Pair("Isekai", "isekai"),
- Pair("Josei", "josei"),
- Pair("Manhua", "manhua"),
- Pair("Manhwa", "manhwa"),
- Pair("Martial arts", "martial-arts"),
- Pair("Moder", "moder"),
- Pair("Mystery", "mystery"),
- Pair("Psychological", "psychological"),
- Pair("Romance", "romance"),
- Pair("School Life", "school-life"),
- Pair("Sci Fi", "sci-fi"),
- Pair("Seinen", "seinen"),
- Pair("Shoujo", "shoujo"),
- Pair("Shounen", "shounen"),
- Pair("Shounen ai", "shounen-ai"),
- Pair("Slice of Life", "slice-of-life"),
- Pair("Super power", "super-power"),
- Pair("Tragedy", "tragedy"),
- Pair("Webtoon", "webtoon"),
- Pair("Webtoons", "webtoons"),
- )
- open class UriPartFilter(displayName: String, private val vals: Array>) :
- Filter.Select(displayName, vals.map { it.first }.toTypedArray()) {
- fun toUriPart() = vals[state].second
- }
-}
diff --git a/src/en/manhwamanga/AndroidManifest.xml b/src/en/manhwamanga/AndroidManifest.xml
deleted file mode 100644
index 30deb7f79..000000000
--- a/src/en/manhwamanga/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/src/en/manhwamanga/build.gradle b/src/en/manhwamanga/build.gradle
deleted file mode 100644
index 8b3d2738a..000000000
--- a/src/en/manhwamanga/build.gradle
+++ /dev/null
@@ -1,12 +0,0 @@
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-
-ext {
- extName = 'ManhwaManga.net'
- pkgNameSuffix = 'en.manhwamanga'
- extClass = '.ManhwaManga'
- extVersionCode = 7
- isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/en/manhwamanga/res/mipmap-hdpi/ic_launcher.png b/src/en/manhwamanga/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index ef5e2ab53..000000000
Binary files a/src/en/manhwamanga/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhwamanga/res/mipmap-mdpi/ic_launcher.png b/src/en/manhwamanga/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 8cca76001..000000000
Binary files a/src/en/manhwamanga/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhwamanga/res/mipmap-xhdpi/ic_launcher.png b/src/en/manhwamanga/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dfafe6efd..000000000
Binary files a/src/en/manhwamanga/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhwamanga/res/mipmap-xxhdpi/ic_launcher.png b/src/en/manhwamanga/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 4d91f8b3e..000000000
Binary files a/src/en/manhwamanga/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhwamanga/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/manhwamanga/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index a92c696a3..000000000
Binary files a/src/en/manhwamanga/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/manhwamanga/res/web_hi_res_512.png b/src/en/manhwamanga/res/web_hi_res_512.png
deleted file mode 100644
index cda085eaf..000000000
Binary files a/src/en/manhwamanga/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/manhwamanga/src/eu/kanade/tachiyomi/extension/en/manhwamanga/ManhwaManga.kt b/src/en/manhwamanga/src/eu/kanade/tachiyomi/extension/en/manhwamanga/ManhwaManga.kt
deleted file mode 100644
index 3e7c75672..000000000
--- a/src/en/manhwamanga/src/eu/kanade/tachiyomi/extension/en/manhwamanga/ManhwaManga.kt
+++ /dev/null
@@ -1,170 +0,0 @@
-package eu.kanade.tachiyomi.extension.en.manhwamanga
-
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.source.model.Filter
-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.toHttpUrlOrNull
-import okhttp3.Request
-import okhttp3.Response
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-
-class ManhwaManga : ParsedHttpSource() {
- override val name = "ManhwaManga.net"
- override val baseUrl = "https://mwmanhwa.net"
- override val lang = "en"
- override val supportsLatest = true
-
- override fun popularMangaSelector() = ".box_list .li_truyen"
- override fun latestUpdatesSelector() = popularMangaSelector()
- override fun searchMangaSelector() = popularMangaSelector()
- override fun chapterListSelector() = "div.content_view div.list-chapters div.list-chapters div.box_list div.chapter-item.row"
-
- override fun popularMangaNextPageSelector() = "div.page_redirect a.active+ a[data-page]"
- override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
- override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
-
- override fun popularMangaRequest(page: Int): Request {
- return GET("$baseUrl/most-views/page/$page", headers)
- }
-
- override fun latestUpdatesRequest(page: Int): Request {
- return GET("$baseUrl/latest-updates/page/$page", headers)
- }
-
- override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
- return if (query.isNotBlank()) {
- GET("$baseUrl/?s=$query", headers)
- } else {
- val url = "$baseUrl/category/".toHttpUrlOrNull()!!.newBuilder()
- filters.forEach { filter ->
- when (filter) {
-
- is GenreFilter -> url.addPathSegment(filter.toUriPart())
- }
- }
- url.addPathSegment("page")
- url.addPathSegment("$page")
- GET(url.toString(), headers)
- }
- }
- override fun popularMangaFromElement(element: Element): SManga {
- val manga = SManga.create()
- manga.setUrlWithoutDomain(element.select("a").attr("href"))
- manga.title = element.select("a").attr("title")
- manga.thumbnail_url = element.select("a img").attr("src")
-
- return manga
- }
- override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
- override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
-
- override fun chapterListParse(response: Response): List {
- return super.chapterListParse(response).reversed()
- }
-
- override fun chapterFromElement(element: Element): SChapter {
- val chapter = SChapter.create()
-
- element.select("a").let {
- chapter.setUrlWithoutDomain(it.attr("href"))
- chapter.name = it.text()
- }
- chapter.date_upload = 0
- return chapter
- }
-
- override fun mangaDetailsParse(document: Document) = SManga.create().apply {
- title = document.select("h1").text()
- description = document.select("div.story-detail-info").text()
- thumbnail_url = document.select("div.box_info div.box_info_left div.img img").attr("src")
- author = document.select(".box_info_right .info-item:nth-child(2)").text()
- // genre = document.select("div.info > div:nth-child(2) > a").joinToString { it.text() }
- status = document.select(".box_info_right .info-item:nth-child(4) span").text().let {
- when {
- it.contains("Ongoing") -> SManga.ONGOING
- it.contains("Completed") -> SManga.COMPLETED
- else -> SManga.UNKNOWN
- }
- }
- }
-
- override fun pageListParse(document: Document): List {
- return document.select("div.content_view_chap > p > img")
- .mapIndexed { i, el -> Page(i, "", el.attr("data-lazy-src")) }
- }
-
- override fun imageUrlRequest(page: Page) = throw Exception("Not used")
- override fun imageUrlParse(document: Document) = throw Exception("Not used")
-
- override fun getFilterList() = FilterList(
- Filter.Header("NOTE: Ignored if using text search!"),
- Filter.Separator(),
- GenreFilter(getGenreList())
- )
- class GenreFilter(vals: Array>) : UriPartFilter("Category", vals)
-
- private fun getGenreList() = arrayOf(
- Pair("All", ""),
- Pair("Action", "action"),
- Pair("Adult", "adult"),
- Pair("Adventure", "adventure"),
- Pair("BL", "bl"),
- Pair("Comedy", "comedy"),
- Pair("Comic", "comic"),
- Pair("Crime", "crime"),
- Pair("Detective", "detective"),
- Pair("Drama", "drama"),
- Pair("Ecchi", "ecchi"),
- Pair("Fantasy", "fantasy"),
- Pair("Gender bender", "gender-bender"),
- Pair("GL", "gl"),
- Pair("Gossip", "gossip"),
- Pair("Harem", "harem"),
- Pair("HentaiVN.Net", "hentaivn"),
- Pair("Historical", "historical"),
- Pair("HoiHentai.Com", "hoihentai-com"),
- Pair("Horror", "horror"),
- Pair("Incest", "incest"),
- Pair("Isekai", "isekai"),
- Pair("Manhua", "manhua"),
- Pair("Martial arts", "martial-arts"),
- Pair("Mature", "mature"),
- Pair("Mecha", "mecha"),
- Pair("Medical", "medical"),
- Pair("Monster/Tentacle", "monster-tentacle"),
- Pair("Mystery", "mystery"),
- Pair("Novel", "novel"),
- Pair("Office Life", "office-life"),
- Pair("One shot", "one-shot"),
- Pair("Psychological", "psychological"),
- Pair("Revenge", "revenge"),
- Pair("Romance", "romance"),
- Pair("School Life", "school-life"),
- Pair("Sci Fi", "sci-fi"),
- Pair("Seinen", "seinen"),
- Pair("Shoujo", "shoujo"),
- Pair("Shounen", "shounen"),
- Pair("Slice of Life", "slice-of-life"),
- Pair("Smut", "smut"),
- Pair("Sports", "sports"),
- Pair("Supernatural", "supernatural"),
- Pair("Teenager", "teenager"),
- Pair("Thriller", "thriller"),
- Pair("Time Travel", "time-travel"),
- Pair("Tragedy", "tragedy"),
- Pair("Uncensored", "uncensored"),
- Pair("Vampire", "vampire"),
- Pair("Webtoon", "webtoon"),
- Pair("Yaoi", "yaoi"),
- Pair("Yuri", "yuri")
- )
- open class UriPartFilter(displayName: String, private val vals: Array>) :
- Filter.Select(displayName, vals.map { it.first }.toTypedArray()) {
- fun toUriPart() = vals[state].second
- }
-}