Bye bye Madara (#5730)

* N-Z

* MZuki-N

* kbatch-mzuki

* remove madara

* remove wildcard

* nsfw

* pkg name

already got it from class name

* forgot className

* this too

* fix arangScans chapter and timestamp

* add mangaEffect

* add mangaGreat

* mangazuki.club multiLang

* forgot this line :v
This commit is contained in:
Riztard Lanthorn
2021-02-10 21:05:12 +07:00
committed by GitHub
parent 532ffb1892
commit bd5f42f8cd
88 changed files with 1022 additions and 1407 deletions

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -1,13 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'Madara (multiple sources)'
pkgNameSuffix = "all.madara"
extClass = '.MadaraFactory'
extVersionCode = 180
libVersion = '1.2'
containsNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

View File

@ -1,525 +0,0 @@
package eu.kanade.tachiyomi.extension.all.madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.source.model.Filter
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 eu.kanade.tachiyomi.util.asJsoup
import okhttp3.CacheControl
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.concurrent.TimeUnit
import kotlin.math.absoluteValue
import kotlin.random.Random
abstract class Madara(
override val name: String,
override val baseUrl: String,
override val lang: String,
private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US)
) : ParsedHttpSource() {
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
// helps with cloudflare for some sources, makes it worse for others; override with empty string if the latter is true
protected open val userAgentRandomizer = " ${Random.nextInt().absoluteValue}"
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/78.0$userAgentRandomizer")
// Popular Manga
override fun popularMangaSelector() = "div.page-item-detail"
open val popularMangaUrlSelector = "div.post-title a"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
with(element) {
select(popularMangaUrlSelector).first()?.let {
manga.setUrlWithoutDomain(it.attr("abs:href"))
manga.title = it.ownText()
}
select("img").first()?.let {
manga.thumbnail_url = imageFromElement(it)
}
}
return manga
}
open fun formBuilder(page: Int, popular: Boolean) = FormBody.Builder().apply {
add("action", "madara_load_more")
add("page", (page - 1).toString())
add("template", "madara-core/content/content-archive")
add("vars[orderby]", "meta_value_num")
add("vars[paged]", "1")
add("vars[posts_per_page]", "20")
add("vars[post_type]", "wp-manga")
add("vars[post_status]", "publish")
add("vars[meta_key]", if (popular) "_wp_manga_views" else "_latest_update")
add("vars[order]", "desc")
add("vars[sidebar]", if (popular) "full" else "right")
add("vars[manga_archives_item_layout]", "big_thumbnail")
}
open val formHeaders: Headers by lazy { headersBuilder().build() }
override fun popularMangaRequest(page: Int): Request {
return POST("$baseUrl/wp-admin/admin-ajax.php", formHeaders, formBuilder(page, true).build(), CacheControl.FORCE_NETWORK)
}
override fun popularMangaNextPageSelector(): String? = "body:not(:has(.no-posts))"
// Latest Updates
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga {
// Even if it's different from the popular manga's list, the relevant classes are the same
return popularMangaFromElement(element)
}
override fun latestUpdatesRequest(page: Int): Request {
return POST("$baseUrl/wp-admin/admin-ajax.php", formHeaders, formBuilder(page, false).build(), CacheControl.FORCE_NETWORK)
}
override fun latestUpdatesNextPageSelector(): String? = popularMangaNextPageSelector()
override fun latestUpdatesParse(response: Response): MangasPage {
val mp = super.latestUpdatesParse(response)
val mangas = mp.mangas.distinctBy { it.url }
return MangasPage(mangas, mp.hasNextPage)
}
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return client.newCall(searchMangaRequest(page, query, filters))
.asObservable().doOnNext { response ->
if (!response.isSuccessful) {
response.close()
// Error message for exceeding last page
if (response.code() == 404)
error("Already on the Last Page!")
else throw Exception("HTTP error ${response.code()}")
}
}
.map { response ->
searchMangaParse(response)
}
}
// Search Manga
protected open fun searchPage(page: Int): String = "page/$page/"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = HttpUrl.parse("$baseUrl/${searchPage(page)}")!!.newBuilder()
url.addQueryParameter("s", query)
url.addQueryParameter("post_type", "wp-manga")
filters.forEach { filter ->
when (filter) {
is AuthorFilter -> {
if (filter.state.isNotBlank()) {
url.addQueryParameter("author", filter.state)
}
}
is ArtistFilter -> {
if (filter.state.isNotBlank()) {
url.addQueryParameter("artist", filter.state)
}
}
is YearFilter -> {
if (filter.state.isNotBlank()) {
url.addQueryParameter("release", filter.state)
}
}
is StatusFilter -> {
filter.state.forEach {
if (it.state) {
url.addQueryParameter("status[]", it.id)
}
}
}
is OrderByFilter -> {
if (filter.state != 0) {
url.addQueryParameter("m_orderby", filter.toUriPart())
}
}
is GenreConditionFilter -> {
url.addQueryParameter("op", filter.toUriPart())
}
is GenreList -> {
filter.state
.filter { it.state }
.let { list ->
if (list.isNotEmpty()) { list.forEach { genre -> url.addQueryParameter("genre[]", genre.id) } }
}
}
}
}
return GET(url.toString(), headers)
}
private class AuthorFilter : Filter.Text("Author")
private class ArtistFilter : Filter.Text("Artist")
private class YearFilter : Filter.Text("Year of Released")
private class StatusFilter(status: List<Tag>) : Filter.Group<Tag>("Status", status)
private class OrderByFilter : UriPartFilter(
"Order By",
arrayOf(
Pair("<select>", ""),
Pair("Latest", "latest"),
Pair("A-Z", "alphabet"),
Pair("Rating", "rating"),
Pair("Trending", "trending"),
Pair("Most Views", "views"),
Pair("New", "new-manga")
)
)
private class GenreConditionFilter : UriPartFilter(
"Genre condition",
arrayOf(
Pair("or", ""),
Pair("and", "1")
)
)
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
class Genre(name: String, val id: String = name) : Filter.CheckBox(name)
open fun getGenreList() = listOf(
Genre("Adventure", "Adventure"),
Genre("Action", "action"),
Genre("Adventure", "adventure"),
Genre("Cars", "cars"),
Genre("4-Koma", "4-koma"),
Genre("Comedy", "comedy"),
Genre("Completed", "completed"),
Genre("Cooking", "cooking"),
Genre("Dementia", "dementia"),
Genre("Demons", "demons"),
Genre("Doujinshi", "doujinshi"),
Genre("Drama", "drama"),
Genre("Ecchi", "ecchi"),
Genre("Fantasy", "fantasy"),
Genre("Game", "game"),
Genre("Gender Bender", "gender-bender"),
Genre("Harem", "harem"),
Genre("Historical", "historical"),
Genre("Horror", "horror"),
Genre("Isekai", "isekai"),
Genre("Josei", "josei"),
Genre("Kids", "kids"),
Genre("Magic", "magic"),
Genre("Manga", "manga"),
Genre("Manhua", "manhua"),
Genre("Manhwa", "manhwa"),
Genre("Martial Arts", "martial-arts"),
Genre("Mature", "mature"),
Genre("Mecha", "mecha"),
Genre("Military", "military"),
Genre("Music", "music"),
Genre("Mystery", "mystery"),
Genre("Old Comic", "old-comic"),
Genre("One Shot", "one-shot"),
Genre("Oneshot", "oneshot"),
Genre("Parodi", "parodi"),
Genre("Parody", "parody"),
Genre("Police", "police"),
Genre("Psychological", "psychological"),
Genre("Romance", "romance"),
Genre("Samurai", "samurai"),
Genre("School", "school"),
Genre("School Life", "school-life"),
Genre("Sci-Fi", "sci-fi"),
Genre("Seinen", "seinen"),
Genre("Shoujo", "shoujo"),
Genre("Shoujo Ai", "shoujo-ai"),
Genre("Shounen", "shounen"),
Genre("Shounen ai", "shounen-ai"),
Genre("Slice of Life", "slice-of-life"),
Genre("Sports", "sports"),
Genre("Super Power", "super-power"),
Genre("Supernatural", "supernatural"),
Genre("Thriller", "thriller"),
Genre("Tragedy", "tragedy"),
Genre("Vampire", "vampire"),
Genre("Webtoons", "webtoons"),
Genre("Yaoi", "yaoi"),
Genre("Yuri", "yuri")
)
override fun getFilterList() = FilterList(
AuthorFilter(),
ArtistFilter(),
YearFilter(),
StatusFilter(getStatusList()),
OrderByFilter(),
Filter.Separator(),
Filter.Header("Genres may not work for all sources"),
GenreConditionFilter(),
GenreList(getGenreList())
)
private fun getStatusList() = listOf(
Tag("end", "Completed"),
Tag("on-going", "Ongoing"),
Tag("canceled", "Canceled"),
Tag("on-hold", "On Hold")
)
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
open class Tag(val id: String, name: String) : Filter.CheckBox(name)
override fun searchMangaSelector() = "div.c-tabs-item__content"
override fun searchMangaFromElement(element: Element): SManga {
val manga = SManga.create()
with(element) {
select("div.post-title a").first()?.let {
manga.setUrlWithoutDomain(it.attr("abs:href"))
manga.title = it.ownText()
}
select("img").first()?.let {
manga.thumbnail_url = imageFromElement(it)
}
}
return manga
}
override fun searchMangaNextPageSelector(): String? = "div.nav-previous, nav.navigation-ajax, a.nextpostslink"
// Manga Details Parse
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
with(document) {
select("div.post-title h3").first()?.let {
manga.title = it.ownText()
}
select("div.author-content").first()?.let {
manga.author = it.text()
}
select("div.artist-content").first()?.let {
manga.artist = it.text()
}
select("div.description-summary div.summary__content").let {
if (it.select("p").text().isNotEmpty()) {
manga.description = it.select("p").joinToString(separator = "\n\n") { p ->
p.text().replace("<br>", "\n")
}
} else {
manga.description = it.text()
}
}
select("div.summary_image img").first()?.let {
manga.thumbnail_url = imageFromElement(it)
}
select("div.summary-content").last()?.let {
manga.status = when (it.text()) {
// I don't know what's the corresponding for COMPLETED and LICENSED
// There's no support for "Canceled" or "On Hold"
"Completed", "Completo", "Concluído", "Tamamlandı", "Tamamlandı." -> SManga.COMPLETED
"OnGoing", "Продолжается", "Updating", "Em Lançamento", "Em andamento", "Devam Ediyor", "Devam Ediyor." -> SManga.ONGOING
else -> SManga.UNKNOWN
}
}
val genres = mutableListOf<String>()
select("div.genres-content a").forEach { element ->
val genre = element.text()
genres.add(genre)
}
manga.genre = genres.joinToString(", ")
}
return manga
}
protected fun imageFromElement(element: Element): String? {
return when {
element.hasAttr("data-src") -> element.attr("abs:data-src")
element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src")
element.hasAttr("srcset") -> element.attr("abs:srcset").substringBefore(" ")
else -> element.attr("abs:src")
}
}
protected fun getXhrChapters(mangaId: String): Document {
val xhrHeaders = headersBuilder().add("Content-Type: application/x-www-form-urlencoded; charset=UTF-8")
.add("Referer", baseUrl)
.build()
val body = RequestBody.create(null, "action=manga_get_chapters&manga=$mangaId")
return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, body)).execute().asJsoup()
}
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
val dataIdSelector = "div[id^=manga-chapters-holder]"
return document.select(chapterListSelector())
.let { elements ->
if (elements.isEmpty() && !document.select(dataIdSelector).isNullOrEmpty())
getXhrChapters(document.select(dataIdSelector).attr("data-id")).select(chapterListSelector())
else elements
}
.map { chapterFromElement(it) }
}
override fun chapterListSelector() = "li.wp-manga-chapter"
open val chapterUrlSelector = "a"
open val chapterUrlSuffix = "?style=list"
open val chapterDatesNewSelector = "img"
override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create()
with(element) {
select(chapterUrlSelector).first()?.let { urlElement ->
chapter.url = urlElement.attr("abs:href").let {
it.substringBefore("?style=paged") + if (!it.endsWith(chapterUrlSuffix)) chapterUrlSuffix else ""
}
chapter.name = urlElement.text()
}
// Dates can be part of a "new" graphic or plain text
chapter.date_upload = select(chapterDatesNewSelector).firstOrNull()?.attr("alt")?.let { parseRelativeDate(it) }
?: parseChapterDate(select("span.chapter-release-date i").firstOrNull()?.text())
}
return chapter
}
open fun parseChapterDate(date: String?): Long {
date ?: return 0
fun SimpleDateFormat.tryParse(string: String): Long {
return try {
parse(string)?.time ?: 0
} catch (_: ParseException) {
0
}
}
return when {
date.endsWith(" ago", ignoreCase = true) -> {
parseRelativeDate(date)
}
// Handle translated 'ago' in Portuguese.
date.endsWith(" atrás", ignoreCase = true) -> {
parseRelativeDate(date)
}
// Handle translated 'ago' in Turkish.
date.endsWith(" önce", ignoreCase = true) -> {
parseRelativeDate(date)
}
// Handle 'yesterday' and 'today', using midnight
date.startsWith("year", ignoreCase = true) -> {
Calendar.getInstance().apply {
add(Calendar.DAY_OF_MONTH, -1) // yesterday
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
}
date.startsWith("today", ignoreCase = true) -> {
Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
}
date.contains(Regex("""\d(st|nd|rd|th)""")) -> {
// Clean date (e.g. 5th December 2019 to 5 December 2019) before parsing it
date.split(" ").map {
if (it.contains(Regex("""\d\D\D"""))) {
it.replace(Regex("""\D"""), "")
} else {
it
}
}
.let { dateFormat.tryParse(it.joinToString(" ")) }
}
else -> dateFormat.tryParse(date)
}
}
// Parses dates in this form:
// 21 horas ago
private fun parseRelativeDate(date: String): Long {
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
val cal = Calendar.getInstance()
return when {
WordSet("يوم", "hari", "gün", "jour", "día", "dia", "day").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
WordSet("ساعات", "jam", "saat", "heure", "hora", "hour").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
WordSet("دقيقة", "menit", "dakika", "min", "minute", "minuto").anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
WordSet("detik", "segundo", "second").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
else -> 0
}
}
override fun pageListRequest(chapter: SChapter): Request {
if (chapter.url.startsWith("http")) {
return GET(chapter.url, headers)
}
return super.pageListRequest(chapter)
}
open val pageListParseSelector = "div.page-break"
override fun pageListParse(document: Document): List<Page> {
return document.select(pageListParseSelector).mapIndexed { index, element ->
Page(
index,
document.location(),
element.select("img").first()?.let {
it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src")
}
)
}
}
override fun imageRequest(page: Page): Request {
return GET(page.imageUrl!!, headers.newBuilder().set("Referer", page.url).build())
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
}
class WordSet(private vararg val words: String) { fun anyWordIn(dateString: String): Boolean = words.any { dateString.contains(it, ignoreCase = true) } }

View File

@ -1,861 +0,0 @@
package eu.kanade.tachiyomi.extension.all.madara
import eu.kanade.tachiyomi.annotations.Nsfw
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
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 okhttp3.CacheControl
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class MadaraFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
// AdonisFansub(),
// AkuManga(),
// AlianzaMarcial(),
// AllPornComic(),
// Aloalivn(),
// AniMangaEs(),
// AoCTranslations(),
// ApollComics(),
// ArangScans(),
// ArazNovel(),
// ArgosScan(),
// AsgardTeam(),
// AstralLibrary(),
// Atikrost(),
// ATMSubs(),
// Azora(),
// Bakaman(),
// BestManga(),
// BestManhua(),
// BoysLove(),
// CatOnHeadTranslations(),
// CatTranslator(),
// ChibiManga(),
// CloverManga(),
// ComicKiba(),
// ComicsValley(),
// CopyPasteScan(),
// CutiePie(),
// DarkyuRealm(),
// DecadenceScans(),
// DetectiveConanAr(),
// DiamondFansub(),
// DisasterScans(),
// DoujinHentai(),
// DoujinYosh(),
// DreamManga(),
// DropeScan(),
// EinherjarScan(),
// FdmScan(),
// FirstKissManga(),
// FirstKissManhua(),
// FreeWebtoonCoins(),
// FurioScans(),
// GeceninLordu(),
// GoldenManga(),
// GrazeScans(),
// GourmetScans(),
// GuncelManga(),
// HeroManhua(),
// HerozScanlation(),
// HikariScan(),
// HimeraFansub(),
// Hiperdex(),
// Hscans(),
// HunterFansub(),
// IchirinNoHanaYuri(),
// ImmortalUpdates(),
// IsekaiScanCom(),
// ItsYourRightManhua(),
// JJutsuScans(),
// JustForFun(),
// KingzManga(),
// KisekiManga(),
// KlikManga(),
// Kombatch(),
KomikGo(),
LilyManga(),
LovableSubs(),
Manga18Fun(),
Manga347(),
Manga3asq(),
Manga3S(),
Manga68(),
MangaAction(),
MangaArabOnline(),
MangaArabTeam(),
MangaBaz(),
MangaBin(),
MangaBob(),
MangaChill(),
MangaClash(),
MangaCrimson(),
MangaCultivator(),
MangaDods(),
MangaHentai(),
MangaKiss(),
MangaKomi(),
MangaLandArabic(),
Mangalek(),
MangaLord(),
ManganeloLink(),
MangaNine(),
MangaOnlineCo(),
MangaPhoenix(),
MangaRave(),
MangaRawr(),
MangaRead(),
MangaReadOrg(),
Mangareceh(),
MangaRockTeam(),
MangaRocky(),
MangaRoma(),
MangaScantrad(),
MangaSco(),
MangaSpark(),
MangaStarz(),
MangaStein(),
Mangasushi(),
MangaSY(),
MangaTeca(),
MangaTurf(),
MangaTX(),
MangaWeebs(),
MangaWT(),
MangaYaku(),
MangaYosh(),
MangazukiClubJP(),
MangazukiClubKO(),
MangazukiMe(),
// MangazukiOnline(),
ManhuaBox(),
ManhuaFast(),
Manhuaga(),
ManhuaPlus(),
Manhuasnet(),
ManhuasWorld(),
ManhuaSY(),
ManhuaUS(),
ManhwaRaw(),
ManhwaTop(),
ManwahentaiMe(),
ManwhaClub(),
ManyToon(),
ManyToonClub(),
ManyToonMe(),
MarkScans(),
MartialScans(),
MGKomik(),
Milftoon(),
MiracleScans(),
MixedManga(),
MMScans(),
MundoWuxia(),
MysticalMerries(),
NazarickScans(),
NeatManga(),
NekoBreaker(),
NekoScan(),
NeoxScanlator(),
NightComic(),
NijiTranslations(),
Ninjavi(),
NTSVoidScans(),
OffScan(),
OlaoeManga(),
OnManga(),
OrigamiOrpheans(),
PMScans(),
PojokManga(),
PornComix(),
PrimeManga(),
ProjetoScanlator(),
QueensManga(),
RaiderScans(),
RandomTranslations(),
RawMangas(),
ReadManhua(),
RenaScans(),
RuyaManga(),
S2Manga(),
SekteDoujin(),
ShoujoHearts(),
SiXiangScans(),
Siyahmelek(),
Skymanga(),
SoloScanlation(),
SpookyScanlations(),
StageComics(),
TheTopComic(),
ThreeSixtyFiveManga(),
ToonGod(),
Toonily(),
ToonilyNet(),
ToonPoint(),
TopManhua(),
TritiniaScans(),
TruyenTranhAudioCom(),
TruyenTranhAudioOnline(),
TsubakiNoScan(),
TurkceManga(),
TwilightScans(),
UyuyanBalik(),
VanguardBun(),
Voidscans(),
Wakascan(),
WarQueenScan(),
WebNovel(),
WebToonily(),
WebtoonXYZ(),
WeScans(),
WoopRead(),
WorldRomanceTranslation(),
WuxiaWorld(),
YaoiToshokan(),
YokaiJump(),
YuriVerso(),
ZinManga(),
ZManga(),
// removed because scanlator site and they requested
// AhStudios(),
// KnightNoScanlation(),
)
}
@Nsfw
class RawMangas : Madara("Raw Mangas", "https://rawmangas.net", "ja", SimpleDateFormat("MMMM dd, yyyy", Locale.US))
class SiXiangScans : Madara("SiXiang Scans", "http://www.sixiangscans.com", "en", SimpleDateFormat("MMMM dd, yyyy", Locale.US))
class SoloScanlation : Madara("SoloScanlation", "https://soloscanlation.site", "en", SimpleDateFormat("MMMM dd, yyyy", Locale.US))
class LovableSubs : Madara("LovableSubs", "https://lovablesubs.com", "tr", SimpleDateFormat("dd MMM yyyy", Locale("tr")))
class MangaOnlineCo : Madara("Manga-Online.co", "https://www.manga-online.co", "th", SimpleDateFormat("MMM dd, yyyy", Locale("th"))) {
override fun chapterListParse(response: Response): List<SChapter> = super.chapterListParse(response).reversed()
}
class NeatManga : Madara("NeatManga", "https://neatmanga.com", "en", SimpleDateFormat("dd MMM yyyy", Locale.US))
class WarQueenScan : Madara("War Queen Scan", "https://wqscan.com", "pt-BR", SimpleDateFormat("dd/MM/yyyy", Locale.US))
class StageComics : Madara("StageComics", "https://stagecomics.com", "pt-BR", SimpleDateFormat("MMMM dd, yyyy", Locale("pt"))) {
override fun chapterFromElement(element: Element): SChapter {
val parsedChapter = super.chapterFromElement(element)
parsedChapter.date_upload = element.select("img").firstOrNull()?.attr("alt")
?.let { parseChapterDate(it) }
?: parseChapterDate(element.select("span.chapter-release-date i").firstOrNull()?.text())
return parsedChapter
}
}
class SpookyScanlations : Madara("Spooky Scanlations", "https://spookyscanlations.xyz", "es", SimpleDateFormat("MMMM dd, yyyy", Locale("es")))
class RandomTranslations : Madara("Random Translations", "https://randomtranslations.com", "en", SimpleDateFormat("dd/MM/yyyy", Locale.US))
class ManhuaFast : Madara("ManhuaFast", "https://manhuafast.com", "en") {
override val pageListParseSelector = "li.blocks-gallery-item"
}
class ManhuaSY : Madara("Manhua SY", "https://www.manhuasy.com", "en")
class MangaRave : Madara("MangaRave", "http://www.mangarave.com", "en")
class NekoScan : Madara("NekoScan", "https://nekoscan.com", "en")
class Manga3S : Madara("Manga3S", "https://manga3s.com", "en")
class MGKomik : Madara("MG Komik", "https://mgkomik.my.id", "id")
class MangaSco : Madara("MangaSco", "https://mangasco.com", "en")
class MangaCrimson : Madara("Manga Crimson", "https://mangacrimson.com", "tr", SimpleDateFormat("dd MMMM yyyy", Locale("tr")))
class PrimeManga : Madara("Prime Manga", "https://primemanga.com", "en")
class NekoBreaker : Madara("NekoBreaker", "https://nekobreaker.com", "pt-BR", SimpleDateFormat("MMMM dd, yyyy", Locale("pt")))
class ManganeloLink : Madara("Manganelo.link", "https://manganelo.link", "en")
class MangaRoma : Madara("MangaRoma", "https://mangaroma.com", "en")
class NTSVoidScans : Madara("NTS Void Scans", "https://ntsvoidscans.com", "en")
class ToonGod : Madara("ToonGod", "https://www.toongod.com", "en", SimpleDateFormat("dd MMM yyyy", Locale.US))
class WebToonily : Madara("WebToonily", "https://webtoonily.com", "en")
class Manga18Fun : Madara("Manga18 Fun", "https://manga18.fun", "en")
class MangaYaku : Madara("MangaYaku", "https://mangayaku.my.id", "id")
class RuyaManga : Madara("Rüya Manga", "https://www.ruyamanga.com", "tr", SimpleDateFormat("dd MMMM yyyy", Locale.forLanguageTag("tr")))
class MangaBaz : Madara("MangaBaz", "https://mangabaz.com", "tr", SimpleDateFormat("dd MMMM yyyy", Locale.forLanguageTag("tr")))
class Manhuaga : Madara("Manhuaga", "https://manhuaga.com", "en")
class MangaCultivator : Madara("MangaCultivator", "https://mangacultivator.com", "en")
class OffScan : Madara(
"Off Scan",
"https://offscan.top",
"pt-BR",
dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US)
)
class MangaClash : Madara(
"Manga Clash",
"https://mangaclash.com",
"en",
dateFormat = SimpleDateFormat("MM/dd/yy", Locale.US)
)
class TritiniaScans : Madara("TritiniaScans", "https://tritinia.com", "en")
class CopyPasteScan : Madara("CopyPasteScan", "https://copypastescan.xyz", "es")
class Mangasushi : Madara("Mangasushi", "https://mangasushi.net", "en")
class MangaRawr : Madara("MangaRawr", "https://mangarawr.com", "en")
class ReadManhua : Madara(
"ReadManhua",
"https://readmanhua.net",
"en",
dateFormat = SimpleDateFormat("dd MMM yy", Locale.US)
)
class KomikGo : Madara("KomikGo", "https://komikgo.com", "id")
class TsubakiNoScan : Madara(
"Tsubaki No Scan",
"https://tsubakinoscan.com",
"fr",
dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US)
)
class YokaiJump : Madara(
"Yokai Jump",
"https://yokaijump.fr",
"fr",
dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US)
)
class ZManga : Madara("ZManga", "https://zmanga.org", "es")
class MangazukiMe : Madara("Mangazuki.me", "https://mangazuki.me", "en")
class MangazukiClubJP : Madara("Mangazuki.club", "https://mangazuki.club", "ja")
class MangazukiClubKO : Madara("Mangazuki.club", "https://mangazuki.club", "ko")
class MangaSY : Madara("Manga SY", "https://www.mangasy.com", "en") {
override fun imageRequest(page: Page): Request = super.imageRequest(page).newBuilder()
.cacheControl(CacheControl.FORCE_NETWORK)
.build()
}
class ManwhaClub : Madara("Manwha Club", "https://manhwa.club", "en")
class WuxiaWorld : Madara("WuxiaWorld", "https://wuxiaworld.site", "en") {
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/tag/webcomic/page/$page/?m_orderby=views", headers)
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/tag/webcomic/page/$page/?m_orderby=latest", headers)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = super.searchMangaRequest(page, "$query comics", filters)
override fun popularMangaNextPageSelector() = "div.nav-previous.float-left"
}
class ManyToon : Madara("ManyToon", "https://manytoon.com", "en")
class ManyToonMe : Madara("ManyToon.me", "https://manytoon.me", "en")
class BoysLove : Madara("BoysLove", "https://boyslove.me", "en")
class ZinManga : Madara("Zin Translator", "https://zinmanga.com", "en") {
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Referer", "https://zinmanga.com/")
}
class ManwahentaiMe : Madara("Manwahentai.me", "https://manhwahentai.me", "en")
class Manga3asq : Madara("مانجا العاشق", "https://3asq.org", "ar")
class Milftoon : Madara("Milftoon", "https://milftoon.xxx", "en") {
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/page/$page/?m_orderby=views", headers)
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/page/$page/?m_orderby=latest", headers)
}
class MangaArabOnline : Madara("Manga Arab Online مانجا عرب اون لاين", "https://mangaarabonline.com", "ar", SimpleDateFormat("MMM d, yyyy", Locale.forLanguageTag("ar")))
class MangaArabTeam : Madara("مانجا عرب تيم Manga Arab Team", "https://mangaarabteam.com", "ar", SimpleDateFormat("dd MMM، yyyy", Locale.forLanguageTag("ar"))) {
override fun imageRequest(page: Page): Request {
return GET(page.imageUrl!!.replace("http:", "https:"))
}
}
class NightComic : Madara("Night Comic", "https://www.nightcomic.com", "en") {
override val formHeaders: Headers = headersBuilder()
.add("Content-Type", "application/x-www-form-urlencoded")
.add("X-MOD-SBB-CTYPE", "xhr")
.build()
}
class Skymanga : Madara("Skymanga", "https://skymanga.co", "en")
@Nsfw
class Toonily : Madara("Toonily", "https://toonily.com", "en") {
override fun getGenreList(): List<Genre> = listOf(
Genre("Action", "action-webtoon"),
Genre("Adult", "adult-webtoon"),
Genre("Adventure", "adventure-webtoon"),
Genre("Comedy", "comedy-webtoon"),
Genre("Drama", "drama-webtoon"),
Genre("Fantasy", "fantasy-webtoon"),
Genre("Gender Bender", "gender-bender"),
Genre("Gossip", "gossip"),
Genre("Harem", "harem-webtoon"),
Genre("Historical", "webtoon-historical"),
Genre("Horror", "horror-webtoon"),
Genre("Josei", "josei-manga"),
Genre("Mature", "mature-webtoon"),
Genre("Mystery", "mystery-webtoon"),
Genre("NTR", "ntr-webtoon"),
Genre("Psychological", "psychological-webtoon"),
Genre("Romance", "romance-webtoon"),
Genre("School life", "school-life-webtoon"),
Genre("Sci-Fi", "scifi-webtoon"),
Genre("Seinen", "seinen-webtoon"),
Genre("Shoujo", "shoujo"),
Genre("Shounen", "shounen-webtoon"),
Genre("Slice of Life", "sliceoflife-webtoon"),
Genre("Supernatural", "supernatural-webtoon"),
Genre("Thriller", "thriller-webtoon"),
Genre("Tragedy", "tragedy"),
Genre("Vanilla", "vanilla-webtoon"),
Genre("Yaoi", "yaoi-webtoon"),
Genre("Yuri", "yuri-webtoon")
)
}
class MangaKomi : Madara("MangaKomi", "https://mangakomi.com", "en")
@Nsfw
class YaoiToshokan : Madara("Yaoi Toshokan", "https://yaoitoshokan.com.br", "pt-BR", SimpleDateFormat("dd MMM yyyy", Locale("pt", "BR"))) {
// Page has custom link to scan website.
override val popularMangaUrlSelector = "div.post-title a:not([target])"
// Chapters are listed old to new.
override fun chapterListParse(response: Response): List<SChapter> {
return super.chapterListParse(response).reversed()
}
override fun pageListParse(document: Document): List<Page> {
return document.select(pageListParseSelector)
.mapIndexed { index, element ->
// Had to add trim because of white space in source.
val imageUrl = element.select("img").attr("data-src").trim()
Page(index, document.location(), imageUrl)
}
}
}
class Mangalek : Madara("مانجا ليك", "https://mangalek.com", "ar", SimpleDateFormat("MMMM dd, yyyy", Locale("ar")))
class AstralLibrary : Madara("Astral Library", "https://www.astrallibrary.net", "en", SimpleDateFormat("d MMM", Locale.US)) {
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/manga-tag/manga/?m_orderby=views&page=$page", headers)
}
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/manga-tag/manga/?m_orderby=latest&page=$page", headers)
}
}
class MiracleScans : Madara("Miracle Scans", "https://miraclescans.com", "en")
class Manhuasnet : Madara("Manhuas.net", "https://manhuas.net", "en")
class MangaTX : Madara("MangaTX", "https://mangatx.com", "en")
class OnManga : Madara("OnManga", "https://onmanga.com", "en")
class MangaAction : Madara("Manga Action", "https://manga-action.com", "ar", SimpleDateFormat("yyyy-MM-dd", Locale("ar")))
class NijiTranslations : Madara("Niji Translations", "https://niji-translations.com", "ar", SimpleDateFormat("MMMM dd, yyyy", Locale("ar")))
class LilyManga : Madara("Lily Manga", "https://lilymanga.com", "en", SimpleDateFormat("yyyy-MM-dd", Locale.US))
class MangaBob : Madara("MangaBob", "https://mangabob.com", "en")
class ThreeSixtyFiveManga : Madara("365Manga", "https://365manga.com", "en") {
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/manga/page/$page/?m_orderby=views", headers)
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/manga/page/$page/?m_orderby=latest", headers)
}
class MangaDods : Madara("MangaDods", "https://www.mangadods.com", "en", SimpleDateFormat("yyyy-MM-dd", Locale.US))
class NeoxScanlator : Madara(
"Neox Scanlator",
"https://neoxscans.com",
"pt-BR",
SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR"))
) {
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.build()
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("User-Agent", USER_AGENT)
.add("Referer", baseUrl)
.add("Origin", baseUrl)
// Only status and order by filter work.
override fun getFilterList(): FilterList = FilterList(super.getFilterList().slice(3..4))
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"
}
}
class MangaLord : Madara("Manga Lord", "https://mangalord.com", "en")
@Nsfw
class PornComix : Madara("PornComix", "https://www.porncomixonline.net", "en")
class PMScans : Madara("PMScans", "https://www.pmscans.com", "en", SimpleDateFormat("MMM-dd-yy", Locale.US))
class MangaRead : Madara("Manga Read", "https://mangaread.co", "en", SimpleDateFormat("yyyy-MM-dd", Locale.US))
class Manga68 : Madara("Manga68", "https://manga68.com", "en") {
override val pageListParseSelector = "div.page-break, div.text-left p"
}
class ManhuaBox : Madara("ManhuaBox", "https://manhuabox.net", "en") {
override val pageListParseSelector = "div.page-break, div.text-left p img"
}
class RaiderScans : Madara("Raider Scans", "https://raiderscans.com", "en")
class PojokManga : Madara("Pojok Manga", "https://pojokmanga.com", "id", SimpleDateFormat("MMM dd, yyyy", Locale.US))
class TopManhua : Madara("Top Manhua", "https://topmanhua.com", "en", SimpleDateFormat("MM/dd/yy", Locale.US)) {
override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl)
}
class ManyToonClub : Madara("ManyToonClub", "https://manytoon.club", "ko")
class ManhuaUS : Madara("ManhuaUS", "https://manhuaus.com", "en") {
override val pageListParseSelector = "li.blocks-gallery-item"
}
class MangaWT : Madara("MangaWT", "https://mangawt.com", "tr")
class MangaRockTeam : Madara("Manga Rock Team", "https://mangarockteam.com", "en")
class MixedManga : Madara("Mixed Manga", "https://mixedmanga.com", "en", SimpleDateFormat("d MMM yyyy", Locale.US)) {
override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl)
}
class ManhuasWorld : Madara("Manhuas World", "https://manhuasworld.com", "en")
class ManhwaRaw : Madara("Manhwa Raw", "https://manhwaraw.com", "ko")
class WeScans : Madara("WeScans", "https://wescans.xyz", "en") {
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/manhua/manga/?m_orderby=views", headers)
override fun popularMangaNextPageSelector(): String? = null
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/manhua/manga/?m_orderby=latest", headers)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = GET("$baseUrl/manhua/?s=$query&post_type=wp-manga")
override fun searchMangaNextPageSelector(): String? = null
override fun getFilterList(): FilterList = FilterList()
}
@Nsfw
class MangaHentai : Madara("Manga Hentai", "https://mangahentai.me", "en")
class MangaPhoenix : Madara("Manga Diyari", "https://mangadiyari.com", "tr") {
// Formerly "Manga Phoenix"
override val id = 4308007020001642101
}
class MartialScans : Madara("Martial Scans", "https://martialscans.com", "en") {
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
with(element) {
select(popularMangaUrlSelector).last()?.let {
manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.ownText()
}
select("img").last()?.let {
manga.thumbnail_url = imageFromElement(it)
}
}
return manga
}
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
}
class MangaYosh : Madara("MangaYosh", "https://mangayosh.xyz", "id", SimpleDateFormat("dd MMM yyyy", Locale.US))
class WebtoonXYZ : Madara("WebtoonXYZ", "https://www.webtoon.xyz", "en") {
private fun pagePath(page: Int) = if (page > 1) "page/$page/" else ""
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/webtoons/${pagePath(page)}?m_orderby=views", headers)
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/webtoons/${pagePath(page)}?m_orderby=latest", headers)
}
class MangaReadOrg : Madara("MangaRead.org", "https://www.mangaread.org", "en", SimpleDateFormat("dd.MM.yyy", Locale.US))
class TurkceManga : Madara("Türkçe Manga", "https://turkcemanga.com", "tr") {
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/page/$page/?s&post_type=wp-manga&m_orderby=views", headers)
override fun popularMangaSelector() = searchMangaSelector()
override fun popularMangaFromElement(element: Element): SManga = searchMangaFromElement(element)
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/page/$page/?s&post_type=wp-manga&m_orderby=latest", headers)
override fun latestUpdatesSelector() = searchMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga = searchMangaFromElement(element)
}
class Manga347 : Madara("Manga347", "https://manga347.com", "en", SimpleDateFormat("d MMM, yyyy", Locale.US)) {
override val pageListParseSelector = "li.blocks-gallery-item"
}
class RenaScans : Madara("Renascence Scans (Renascans)", "https://new.renascans.com", "en", SimpleDateFormat("dd/MM/yyyy", Locale.US))
class QueensManga : Madara("QueensManga ملكات المانجا", "https://queensmanga.com", "ar") {
override fun chapterListSelector(): String = "div.listing-chapters_wrap a"
}
class TheTopComic : Madara("TheTopComic", "https://thetopcomic.com", "en")
class WebNovel : Madara("WebNovel", "https://webnovel.live", "en")
class TruyenTranhAudioCom : Madara("TruyenTranhAudio.com", "https://truyentranhaudio.com", "vi", SimpleDateFormat("dd/MM/yyyy", Locale.getDefault())) {
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/page/$page?s&post_type=wp-manga&m_orderby=views", headers)
override fun popularMangaSelector() = searchMangaSelector()
override fun popularMangaFromElement(element: Element): SManga = searchMangaFromElement(element)
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/page/$page?s&post_type=wp-manga&m_orderby=latest", headers)
override fun latestUpdatesSelector() = searchMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga = searchMangaFromElement(element)
override fun pageListParse(document: Document): List<Page> {
return document.select("div.reading-content img").map { it.attr("abs:src") }
.filterNot { it.isNullOrEmpty() }
.distinct()
.mapIndexed { i, url -> Page(i, "", url) }
}
}
class TruyenTranhAudioOnline : Madara("TruyenTranhAudio.online", "https://truyentranhaudio.online", "vi", SimpleDateFormat("dd/MM/yyyy", Locale.getDefault())) {
override val formHeaders: Headers = headersBuilder()
.add("Content-Type", "application/x-www-form-urlencoded")
.build()
override fun pageListParse(document: Document): List<Page> {
return document.select("div.reading-content img").map { it.attr("abs:src") }
.filterNot { it.isNullOrEmpty() }
.distinct()
.mapIndexed { i, url -> Page(i, "", url) }
}
}
class MangaTurf : Madara("Manga Turf", "https://mangaturf.com", "en")
class Mangareceh : Madara("Mangareceh", "https://mangareceh.id", "id")
class ToonPoint : Madara("ToonPoint", "https://toonpoint.com", "en") {
override val userAgentRandomizer = ""
}
class MangaScantrad : Madara("Manga-Scantrad", "https://manga-scantrad.net", "fr", SimpleDateFormat("d MMM yyyy", Locale.FRANCE))
class ManhuaPlus : Madara("Manhua Plus", "https://manhuaplus.com", "en") {
override val pageListParseSelector = "li.blocks-gallery-item"
}
@Nsfw
class ToonilyNet : Madara("Toonily.net", "https://toonily.net", "en")
class TwilightScans : Madara("Twilight Scans", "https://twilightscans.com", "en")
// mostly novels
class WoopRead : Madara("WoopRead", "https://woopread.com", "en")
class Ninjavi : Madara("Ninjavi", "https://ninjavi.com", "ar")
class ManhwaTop : Madara("Manhwatop", "https://manhwatop.com", "en")
class Wakascan : Madara("Wakascan", "https://wakascan.com", "fr")
class ShoujoHearts : Madara("ShoujoHearts", "http://shoujohearts.com", "en") {
override fun popularMangaRequest(page: Int): Request =
POST("$baseUrl/reader/wp-admin/admin-ajax.php", formHeaders, formBuilder(page, true).build(), CacheControl.FORCE_NETWORK)
override fun latestUpdatesRequest(page: Int): Request =
POST("$baseUrl/reader/wp-admin/admin-ajax.php", formHeaders, formBuilder(page, false).build(), CacheControl.FORCE_NETWORK)
override fun searchPage(page: Int): String = "reader/page/$page/"
}
class OlaoeManga : Madara("مانجا اولاو", "https://olaoe.giize.com", "ar")
class OrigamiOrpheans : Madara("Origami Orpheans", "https://origami-orpheans.com.br", "pt-BR")
class MangaKiss : Madara("Manga Kiss", "https://mangakiss.org", "en")
class MangaRocky : Madara("Manga Rocky", "https://mangarocky.com", "en")
class S2Manga : Madara("S2Manga", "https://s2manga.com", "en")
class MangaLandArabic : Madara("Manga Land Arabic", "https://mangalandarabic.com", "ar")
class ProjetoScanlator : Madara("Projeto Scanlator", "https://projetoscanlator.com", "pt-BR", SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")))
class NazarickScans : Madara("Nazarick Scans", "https://nazarickscans.com", "en") {
override fun popularMangaRequest(page: Int) = GET("$baseUrl/manga/page/$page/?m_orderby=trending", headers)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/manga/page/$page/?m_orderby=trending", headers)
override fun popularMangaNextPageSelector(): String? = null
override fun latestUpdatesNextPageSelector(): String? = null
}
class MangaSpark : Madara("MangaSpark", "https://mangaspark.com", "ar") {
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
with(element) {
select(popularMangaUrlSelector).first()?.let {
manga.setUrlWithoutDomain(it.attr("abs:href"))
manga.title = it.ownText()
}
select("img").first()?.let {
manga.thumbnail_url = imageFromElement(it)?.replace("mangaspark", "mangalek")
}
}
return manga
}
}
class MangaStarz : Madara("Manga Starz", "https://mangastarz.com", "ar") {
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
with(element) {
select(popularMangaUrlSelector).first()?.let {
manga.setUrlWithoutDomain(it.attr("abs:href"))
manga.title = it.ownText()
}
select("img").first()?.let {
manga.thumbnail_url = imageFromElement(it)?.replace("mangastarz", "mangalek")
}
}
return manga
}
}
class MysticalMerries : Madara("Mystical Merries", "https://mysticalmerries.com", "en") {
override fun popularMangaRequest(page: Int) = GET("$baseUrl/genre/manhwa/page/$page/?m_orderby=trending", headers)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/genre/manhwa/page/$page/?m_orderby=latest", headers)
}
class MangaNine : Madara("Manga Nine", "https://manganine.com", "en")
class MarkScans : Madara("Mark Scans", "https://markscans.online", "pt-BR")
class YuriVerso : Madara(
"Yuri Verso",
"https://yuri.live",
"pt-BR",
SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR"))
)
class MangaStein : Madara("MangaStein", "https://mangastein.com", "tr")
class MangaTeca : Madara(
"MangaTeca",
"https://www.mangateca.com",
"pt-BR",
SimpleDateFormat("MMMM dd, yyyy", Locale("pt", "BR"))
) {
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("User-Agent", USER_AGENT)
.add("Referer", baseUrl)
.add("Origin", baseUrl)
override fun chapterFromElement(element: Element): SChapter {
val parsedChapter = super.chapterFromElement(element)
parsedChapter.date_upload = element.select("img").firstOrNull()?.attr("alt")
?.let { parseChapterDate(it) }
?: parseChapterDate(element.select("span.chapter-release-date i").firstOrNull()?.text())
return parsedChapter
}
companion object {
private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36"
}
}
class Voidscans : Madara("Void Scans", "https://voidscans.com", "en") {
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = GET("$baseUrl/?s=$query&post_type=wp-manga")
}
class UyuyanBalik : Madara("Uyuyan Balik", "https://uyuyanbalik.com/", "tr", SimpleDateFormat("dd MMMM yyyy", Locale.US))
class MangaWeebs : Madara("Manga Weebs", "https://mangaweebs.in", "en")
class MMScans : Madara("MMScans", "https://mm-scans.com/", "en")
@Nsfw
class Siyahmelek : Madara("Siyahmelek", "https://siyahmelek.com", "tr", SimpleDateFormat("dd MMM yyyy", Locale("tr")))
@Nsfw
class SekteDoujin : Madara("Sekte Doujin", "https://sektedoujin.xyz", "id")
class MundoWuxia : Madara("Mundo Wuxia", "https://mundowuxia.com", "es", SimpleDateFormat("MMMM dd, yyyy", Locale("es")))
class WorldRomanceTranslation : Madara("World Romance Translation", "https://wrt.my.id/", "id", SimpleDateFormat("dd MMMM yyyy", Locale("id")))
class VanguardBun : Madara("Vanguard Bun", "https://vanguardbun.com/", "en")
class MangaBin : Madara("Manga Bin", "https://mangabin.com/", "en")
class MangaChill : Madara("Manga Chill", "https://mangachill.com/", "en")