diff --git a/multisrc/overrides/res/madara/default/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/res/madara/default/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..bc8eef503 Binary files /dev/null and b/multisrc/overrides/res/madara/default/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/res/madara/default/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/res/madara/default/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..e51d65390 Binary files /dev/null and b/multisrc/overrides/res/madara/default/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/res/madara/default/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/res/madara/default/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..b011eaf9b Binary files /dev/null and b/multisrc/overrides/res/madara/default/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/res/madara/default/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/res/madara/default/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..dac0772db Binary files /dev/null and b/multisrc/overrides/res/madara/default/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/res/madara/default/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/res/madara/default/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..c2706aa3f Binary files /dev/null and b/multisrc/overrides/res/madara/default/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/res/madara/default/web_hi_res_512.png b/multisrc/overrides/res/madara/default/web_hi_res_512.png new file mode 100644 index 000000000..a62cfe72f Binary files /dev/null and b/multisrc/overrides/res/madara/default/web_hi_res_512.png differ diff --git a/multisrc/overrides/src/madara/adonisfansub/AdonisFansub.kt b/multisrc/overrides/src/madara/adonisfansub/AdonisFansub.kt new file mode 100644 index 000000000..773c378bd --- /dev/null +++ b/multisrc/overrides/src/madara/adonisfansub/AdonisFansub.kt @@ -0,0 +1,13 @@ +package eu.kanade.tachiyomi.extension.tr.adonisfansub + +import eu.kanade.tachiyomi.annotations.Nsfw +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import okhttp3.Request + +@Nsfw +class AdonisFansub : Madara("Adonis Fansub", "https://manga.adonisfansub.com", "tr") { + override val userAgentRandomizer = "" + 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) +} diff --git a/multisrc/overrides/src/madara/agentofchangetranslations/AgentofChangeTranslations.kt b/multisrc/overrides/src/madara/agentofchangetranslations/AgentofChangeTranslations.kt new file mode 100644 index 000000000..c2e165e8e --- /dev/null +++ b/multisrc/overrides/src/madara/agentofchangetranslations/AgentofChangeTranslations.kt @@ -0,0 +1,43 @@ +package eu.kanade.tachiyomi.extension.en.agentofchangetranslations + +import android.annotation.SuppressLint +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Headers +import okhttp3.Response + +class AgentofChangeTranslations : Madara("Agent of Change Translations", "https://aoc.moe", "en") { + override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl) + override fun popularMangaSelector() = "div.page-item-detail.manga:has(span.chapter)" + override fun chapterListSelector() = "li.wp-manga-chapter:has(a)" + + @SuppressLint("DefaultLocale") + override fun chapterListParse(response: Response): List { + return response.asJsoup().let { document -> + document.select(chapterListSelector()).let { normalChapters -> + if (normalChapters.isNotEmpty()) { + normalChapters.map { chapterFromElement(it) } + } else { + // For their "fancy" volume/chapter lists + document.select("div.wpb_wrapper:contains(volume) a") + .filter { it.attr("href").contains(baseUrl) && !it.attr("href").contains("imgur") } + .map { volumeChapter -> + SChapter.create().apply { + volumeChapter.attr("href").let { url -> + name = if (url.contains("volume")) { + val volume = url.substringAfter("volume-").substringBefore("/") + val volChap = url.substringAfter("volume-$volume/").substringBefore("/").replace("-", " ").capitalize() + "Volume $volume - $volChap" + } else { + url.substringBefore("/p").substringAfterLast("/").replace("-", " ").capitalize() + } + setUrlWithoutDomain(url.substringBefore("?") + "?style=list") + } + } + } + }.reversed() + } + } + } +} diff --git a/multisrc/overrides/src/madara/allporncomic/AllPornComic.kt b/multisrc/overrides/src/madara/allporncomic/AllPornComic.kt new file mode 100644 index 000000000..9b3c39300 --- /dev/null +++ b/multisrc/overrides/src/madara/allporncomic/AllPornComic.kt @@ -0,0 +1,151 @@ +package eu.kanade.tachiyomi.extension.en.allporncomic + +import eu.kanade.tachiyomi.annotations.Nsfw +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import okhttp3.Request + +@Nsfw +class AllPornComic : Madara("AllPornComic", "https://allporncomic.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) + override fun searchMangaNextPageSelector() = "a[rel=next]" + override fun getGenreList() = listOf( + Genre("3D", "3d"), + Genre("Ahegao", "ahegao"), + Genre("Alien Girl", "alien-girl"), + Genre("Anal", "anal"), + Genre("Anime", "anime"), + Genre("Anthology", "anthology"), + Genre("Artbook", "artbook"), + Genre("BBW / Chubby / Fat Woman", "bbw"), + Genre("BDSM", "bdsm"), + Genre("Big Areolae", "big-areolae"), + Genre("Big Ass", "big-ass"), + Genre("Big Balls", "big-balls"), + Genre("Big Breasts", "big-breasts"), + Genre("Big Clit", "big-clit"), + Genre("Big Nipples", "big-nipples"), + Genre("Big Penis", "big-penis"), + Genre("Bikini", "bikini"), + Genre("Blackmail", "blackmail"), + Genre("Blindfold", "blindfold"), + Genre("Body Modification", "body-modification"), + Genre("Body Swap", "body-swap"), + Genre("Body Writing", "body-writing"), + Genre("BodyStocking", "bodystocking"), + Genre("Bodysuit", "bodysuit"), + Genre("Bondage", "bondage"), + Genre("Brain Fuck", "brain-fuck"), + Genre("Cartoon", "cartoon"), + Genre("Cheerleader", "cheerleader"), + Genre("Chinese Dress", "chinese-dress"), + Genre("Collar / Choker", "collar"), + Genre("Comedy", "comedy"), + Genre("Corruption", "corruption"), + Genre("Corset", "corset"), + Genre("Crotch Tattoo", "crotch-tattoo"), + Genre("Dark Skin", "dark-skin"), + Genre("Demon Girl / Succubus", "demon-girl"), + Genre("Dick Growth", "dick-growth"), + Genre("Dickgirl On Dickgirl", "dickgirl-on-dickgirl"), + Genre("Dickgirl On Male", "dickgirl-on-male"), + Genre("Dickgirls Only", "dickgirls-only"), + Genre("Drugs", "drugs"), + Genre("Drunk", "drunk"), + Genre("Exhibitionism", "exhibitionism"), + Genre("FFM Threesome", "ffm-threesome"), + Genre("FFT Threesome", "fft-threesome"), + Genre("Females Only", "females-only"), + Genre("Femdom", "femdom"), + Genre("Feminization", "feminization"), + Genre("Full Body Tattoo", "full-body-tattoo"), + Genre("Full Color", "full-color"), + Genre("Futanari", "futanari"), + Genre("Gender Bender", "gender-bender"), + Genre("Glasses", "glasses"), + Genre("Group", "group"), + Genre("Gyaru", "gyaru"), + Genre("Gyaru-OH", "gyaru-oh"), + Genre("Harem", "harem"), + Genre("Hentai", "hentai"), + Genre("Human Pet", "human-pet"), + Genre("Humiliation", "humiliation"), + Genre("Impregnation", "impregnation"), + Genre("Incest", "incest"), + Genre("Interracial", "interracial"), + Genre("Kimono", "kimono"), + Genre("Latex", "latex"), + Genre("Leash", "leash"), + Genre("Lingerie", "lingerie"), + Genre("Lolicon", "lolicon"), + Genre("MILF", "milf"), + Genre("MMF Threesome", "mmf-threesome"), + Genre("MMT Threesome", "mmt-threesome"), + Genre("Magical Girl", "magical-girl"), + Genre("Maid", "maid"), + Genre("Male On Dickgirl", "male-on-dickgirl"), + Genre("Manhwa", "manhwa"), + Genre("Military", "military"), + Genre("Milking", "milking"), + Genre("Mind Break", "mind-break"), + Genre("Mind Control", "mind-control"), + Genre("Monster Girl", "monster-girl"), + Genre("Moral Degeneration", "moral-degeneration"), + Genre("Muscle", "muscle"), + Genre("Muscle Growth", "muscle-growth"), + Genre("Nakadashi", "nakadashi"), + Genre("Netorare", "netorare"), + Genre("Netori", "netori"), + Genre("Ninja", "ninja"), + Genre("Nun", "nun"), + Genre("Nurse", "nurse"), + Genre("Orgy", "orgy"), + Genre("Paizuri", "paizuri"), + Genre("Pegging", "pegging"), + Genre("Piercing", "piercing"), + Genre("Pixie Cut", "pixie-cut"), + Genre("Policewoman", "policewoman"), + Genre("Possession", "possession"), + Genre("Retro", "retro"), + Genre("Ryona", "ryona"), + Genre("School Swimsuit", "school-swimsuit"), + Genre("Schoolboy Uniform", "schoolboy-uniform"), + Genre("Schoolgirl Uniform", "schoolgirl-uniform"), + Genre("Shared Senses", "shared-senses"), + Genre("Shemale", "shemale"), + Genre("Shibari", "shibari"), + Genre("Shotacon", "shotacon"), + Genre("Slave", "slave"), + Genre("Slime Girl", "slime-girl"), + Genre("Small Breasts", "small-breasts"), + Genre("Stockings", "stockings"), + Genre("Strap-on", "strap-on"), + Genre("Stuck In Wall", "stuck-in-wall"), + Genre("Superhero", "superhero"), + Genre("Superheroine", "superheroine"), + Genre("Tail", "tail"), + Genre("Tail Plug", "tail-plug"), + Genre("Tankoubon", "tankoubon"), + Genre("Tentacles", "tentacles"), + Genre("Thigh High Boots", "thigh-high-boots"), + Genre("Tights", "tights"), + Genre("Time Stop", "time-stop"), + Genre("Tomboy", "tomboy"), + Genre("Tomgirl", "tomgirl"), + Genre("Torture", "torture"), + Genre("Transformation", "transformation"), + Genre("Uncensored", "uncensored"), + Genre("Unusual Pupils", "unusual-pupils"), + Genre("Unusual Teeth", "unusual-teeth"), + Genre("Vampire", "vampire"), + Genre("Virginity", "virginity"), + Genre("Voyeurism", "voyeurism"), + Genre("Webtoon", "webtoon"), + Genre("Western", "western"), + Genre("Witch", "witch"), + Genre("Yandere", "yandere"), + Genre("Yaoi", "yaoi"), + Genre("Yuri", "yuri") + ) +} diff --git a/multisrc/overrides/src/madara/aloalivn/Aloalivn.kt b/multisrc/overrides/src/madara/aloalivn/Aloalivn.kt new file mode 100644 index 000000000..e31cba4a6 --- /dev/null +++ b/multisrc/overrides/src/madara/aloalivn/Aloalivn.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.en.aloalivn + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class Aloalivn : Madara("Aloalivn", "https://aloalivn.com", "en") { + override val pageListParseSelector = "li.blocks-gallery-item" +} diff --git a/multisrc/overrides/src/madara/animangaes/AniMangaEs.kt b/multisrc/overrides/src/madara/animangaes/AniMangaEs.kt new file mode 100644 index 000000000..6b7f0d96c --- /dev/null +++ b/multisrc/overrides/src/madara/animangaes/AniMangaEs.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.extension.en.animangaes + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.SChapter +import okhttp3.Response + +class AniMangaEs : Madara("AniMangaEs", "http://animangaes.com", "en") { + override val pageListParseSelector = "div.text-left noscript" + override val chapterUrlSuffix = "" + override fun chapterListParse(response: Response): List = super.chapterListParse(response).reversed() +} diff --git a/multisrc/overrides/src/madara/apollcomics/ApollComics.kt b/multisrc/overrides/src/madara/apollcomics/ApollComics.kt new file mode 100644 index 000000000..6f66f2c8b --- /dev/null +++ b/multisrc/overrides/src/madara/apollcomics/ApollComics.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.es.apollcomics + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class ApollComics : Madara("ApollComics", "https://apollcomics.xyz", "es", SimpleDateFormat("dd MMMM, yyyy", Locale("es"))) diff --git a/multisrc/overrides/src/madara/arangscans/ArangScans.kt b/multisrc/overrides/src/madara/arangscans/ArangScans.kt new file mode 100644 index 000000000..f4398f523 --- /dev/null +++ b/multisrc/overrides/src/madara/arangscans/ArangScans.kt @@ -0,0 +1,15 @@ +package eu.kanade.tachiyomi.extension.en.arangscans + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import okhttp3.Request +import java.text.SimpleDateFormat +import java.util.Locale + +class ArangScans : Madara("Arang Scans", "https://www.arangscans.com", "en", SimpleDateFormat("d MMM yyyy", Locale.US)) { + // has very few manga + override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/manga?m_orderby=views", headers) + override fun popularMangaNextPageSelector(): String? = null + override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/manga?m_orderby=latest", headers) + override fun latestUpdatesNextPageSelector(): String? = null +} diff --git a/multisrc/overrides/src/madara/araznovel/ArazNovel.kt b/multisrc/overrides/src/madara/araznovel/ArazNovel.kt new file mode 100644 index 000000000..f13e251de --- /dev/null +++ b/multisrc/overrides/src/madara/araznovel/ArazNovel.kt @@ -0,0 +1,74 @@ +package eu.kanade.tachiyomi.extension.tr.araznovel + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.FormBody +import okhttp3.Response +import java.text.SimpleDateFormat +import java.util.Locale + +class ArazNovel : Madara("ArazNovel", "https://www.araznovel.com", "tr", SimpleDateFormat("dd/MM/yyyy", Locale.getDefault())) { + override fun formBuilder(page: Int, popular: Boolean): FormBody.Builder = super.formBuilder(page, popular) + .add("vars[meta_query][0][0][value]", "manga") + + override fun getGenreList() = listOf( + Genre("Aksiyon", "action"), + Genre("Macera", "adventure"), + Genre("Cartoon", "cartoon"), + Genre("Comic", "comic"), + Genre("Komedi", "comedy"), + Genre("Yemek", "cooking"), + Genre("Doujinshi", "doujinshi"), + Genre("Dram", "drama"), + Genre("Ecchi", "ecchi"), + Genre("Fantastik", "fantasy"), + Genre("Harem", "harem"), + Genre("Tarihi", "historical"), + Genre("Korku", "horror"), + Genre("Manga", "manga"), + Genre("Manhua", "manhua"), + Genre("Manhwa", "manhwa"), + Genre("Olgun", "mature"), + Genre("Mecha", "mecha"), + Genre("Yetişkin", "adult"), + Genre("Gizem", "mystery"), + Genre("One Shot", "one-shot"), + Genre("Isekai", "isekai"), + Genre("Josei", "josei"), + Genre("Dedektif", "detective"), + Genre("Karanlık", "smut"), + Genre("Romantizm", "romance"), + Genre("Okul Yaşamı", "school-life"), + Genre("Yaşamdan Kesit", "slice-of-life"), + Genre("Spor", "sports"), + Genre("Doğa Üstü", "supernatural"), + Genre("Trajedi", "tragedy"), + Genre("Webtoon ", "webtoon"), + Genre("Dövüş Sanatları ", "martial-arts"), + Genre("Bilim Kurgu", "sci-fi"), + Genre("Seinen", "seinen"), + Genre("Shoujo", "shoujo"), + Genre("Shoujo Ai", "shoujo-ai"), + Genre("Shounen", "shounen"), + Genre("Shounen Ai", "shounen-ai"), + Genre("Soft Yaoi", "soft-yaoi"), + Genre("Soft Yuri", "soft-yuri"), + Genre("Yaoi", "yaoi"), + Genre("Yuri", "yuri") + ) + + override fun chapterListParse(response: Response): List { + return getXhrChapters(response.asJsoup().select("div#manga-chapters-holder").attr("data-id")).let { document -> + document.select("li.parent").let { elements -> + if (!elements.isNullOrEmpty()) { + elements.reversed() + .map { volumeElement -> volumeElement.select(chapterListSelector()).map { chapterFromElement(it) } } + .flatten() + } else { + document.select(chapterListSelector()).map { chapterFromElement(it) } + } + } + } + } +} diff --git a/multisrc/overrides/src/madara/argosscan/ArgosScan.kt b/multisrc/overrides/src/madara/argosscan/ArgosScan.kt new file mode 100644 index 000000000..0acad44bc --- /dev/null +++ b/multisrc/overrides/src/madara/argosscan/ArgosScan.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.pt.argosscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class ArgosScan : Madara("Argos Scan", "https://argosscan.com", "pt-BR", SimpleDateFormat("dd 'de' MMMM 'de' yyyy", Locale("pt", "BR"))) diff --git a/multisrc/overrides/src/madara/astrallibrary/AstralLibrary.kt b/multisrc/overrides/src/madara/astrallibrary/AstralLibrary.kt new file mode 100644 index 000000000..d6bb33195 --- /dev/null +++ b/multisrc/overrides/src/madara/astrallibrary/AstralLibrary.kt @@ -0,0 +1,20 @@ +package eu.kanade.tachiyomi.extension.en.astrallibrary + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.SChapter +import okhttp3.Request +import okhttp3.Response +import java.text.SimpleDateFormat +import java.util.Locale + +class AstralLibrary : Madara("Astral Library", "https://www.astrallibrary.net", "en", SimpleDateFormat("d MMM", Locale.US)) { + override fun chapterListParse(response: Response): List = super.chapterListParse(response).reversed() + 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) + } +} diff --git a/multisrc/overrides/src/madara/atikrost/Atikrost.kt b/multisrc/overrides/src/madara/atikrost/Atikrost.kt new file mode 100644 index 000000000..f20e4eebd --- /dev/null +++ b/multisrc/overrides/src/madara/atikrost/Atikrost.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.tr.atikrost + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class Atikrost : Madara("Atikrost", "https://atikrost.com", "tr", SimpleDateFormat("MMMM dd, yyyy", Locale("tr"))) diff --git a/multisrc/overrides/src/madara/atmsubs/ATMSubs.kt b/multisrc/overrides/src/madara/atmsubs/ATMSubs.kt new file mode 100644 index 000000000..efd6dc9f5 --- /dev/null +++ b/multisrc/overrides/src/madara/atmsubs/ATMSubs.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.fr.atmsubs + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class ATMSubs : Madara("ATM-Subs", "https://atm-subs.fr", "fr", SimpleDateFormat("d MMMM yyyy", Locale("fr"))) diff --git a/multisrc/overrides/src/madara/azora/Azora.kt b/multisrc/overrides/src/madara/azora/Azora.kt new file mode 100644 index 000000000..26307e2b3 --- /dev/null +++ b/multisrc/overrides/src/madara/azora/Azora.kt @@ -0,0 +1,22 @@ +package eu.kanade.tachiyomi.extension.fr.atmsubs + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.SChapter +import okhttp3.Request +import org.jsoup.nodes.Element + + +class Azora : Madara("Azora", "https://www.azoramanga.com", "ar") { + override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/page/$page/?m_orderby=views", headers) + override fun chapterListSelector() = "li.wp-manga-chapter:not(.premium-block)" // Filter fake chapters + override fun chapterFromElement(element: Element): SChapter { + val chapter = SChapter.create() + + element.select("a").let { + chapter.url = it.attr("href").substringAfter(baseUrl) + chapter.name = it.text() + } + return chapter + } +} diff --git a/multisrc/overrides/src/madara/bestmanga/BestManga.kt b/multisrc/overrides/src/madara/bestmanga/BestManga.kt new file mode 100644 index 000000000..76fb943fb --- /dev/null +++ b/multisrc/overrides/src/madara/bestmanga/BestManga.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.ru.bestmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class BestManga : Madara("BestManga", "https://bestmanga.club", "ru", SimpleDateFormat("dd.MM.yyyy", Locale.getDefault())) diff --git a/multisrc/overrides/src/madara/bestmanhua/BestManhua.kt b/multisrc/overrides/src/madara/bestmanhua/BestManhua.kt new file mode 100644 index 000000000..b54472fa0 --- /dev/null +++ b/multisrc/overrides/src/madara/bestmanhua/BestManhua.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.en.bestmanhua + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class BestManhua : Madara("BestManhua", "https://bestmanhua.com", "en") { + override val pageListParseSelector = "li.blocks-gallery-item" +} diff --git a/multisrc/overrides/src/madara/cattranslator/CatTranslator.kt b/multisrc/overrides/src/madara/cattranslator/CatTranslator.kt new file mode 100644 index 000000000..5856600bd --- /dev/null +++ b/multisrc/overrides/src/madara/cattranslator/CatTranslator.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.th.cattranslator + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.POST +import okhttp3.CacheControl +import okhttp3.Request + +class CatTranslator : Madara("CAT-translator", "https://cat-translator.com", "th") { + override fun popularMangaRequest(page: Int): Request = + POST("$baseUrl/manga/wp-admin/admin-ajax.php", formHeaders, formBuilder(page, true).build(), CacheControl.FORCE_NETWORK) + + override fun latestUpdatesRequest(page: Int): Request = + POST("$baseUrl/manga/wp-admin/admin-ajax.php", formHeaders, formBuilder(page, false).build(), CacheControl.FORCE_NETWORK) + + override fun searchPage(page: Int): String = "manga/page/$page/" +} diff --git a/multisrc/overrides/src/madara/chibimanga/ChibiManga.kt b/multisrc/overrides/src/madara/chibimanga/ChibiManga.kt new file mode 100644 index 000000000..571f1c479 --- /dev/null +++ b/multisrc/overrides/src/madara/chibimanga/ChibiManga.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.en.chibimanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class ChibiManga : Madara("Chibi Manga", "https://www.cmreader.info", "en", dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale.US)) diff --git a/multisrc/overrides/src/madara/clovermanga/CloverManga.kt b/multisrc/overrides/src/madara/clovermanga/CloverManga.kt new file mode 100644 index 000000000..77f5bd697 --- /dev/null +++ b/multisrc/overrides/src/madara/clovermanga/CloverManga.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.en.chibimanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class CloverManga : Madara("Clover Manga", "Clover Manga", "tr", SimpleDateFormat("MMMM dd, yyyy", Locale("tr"))) diff --git a/multisrc/overrides/src/madara/comickiba/ComicKiba.kt b/multisrc/overrides/src/madara/comickiba/ComicKiba.kt new file mode 100644 index 000000000..c1d13e737 --- /dev/null +++ b/multisrc/overrides/src/madara/comickiba/ComicKiba.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.en.comickiba + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ComicKiba : Madara("ComicKiba", "https://comickiba.com", "en") { + override val pageListParseSelector = "li.blocks-gallery-item img:nth-child(1), div.reading-content p > img, .read-container .reading-content img" +} diff --git a/multisrc/overrides/src/madara/cutiepie/CutiePie.kt b/multisrc/overrides/src/madara/cutiepie/CutiePie.kt new file mode 100644 index 000000000..3a455c070 --- /dev/null +++ b/multisrc/overrides/src/madara/cutiepie/CutiePie.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.tr.cutiepie + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class CutiePie : Madara("Cutie Pie", "https://cutiepie.ga", "tr", SimpleDateFormat("dd MMMM yyyy", Locale.forLanguageTag("tr"))) diff --git a/multisrc/overrides/src/madara/diamondfansub/DiamondFansub.kt b/multisrc/overrides/src/madara/diamondfansub/DiamondFansub.kt new file mode 100644 index 000000000..a75b16163 --- /dev/null +++ b/multisrc/overrides/src/madara/diamondfansub/DiamondFansub.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.tr.diamondfansub + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class DiamondFansub : Madara("DiamondFansub", "https://diamondfansub.com", "tr", SimpleDateFormat("MMMM dd, yyyy", Locale.forLanguageTag("tr"))) diff --git a/multisrc/overrides/src/madara/disasterscans/DisasterScans.kt b/multisrc/overrides/src/madara/disasterscans/DisasterScans.kt new file mode 100644 index 000000000..e67ec2705 --- /dev/null +++ b/multisrc/overrides/src/madara/disasterscans/DisasterScans.kt @@ -0,0 +1,21 @@ +package eu.kanade.tachiyomi.extension.en.disasterscans + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.SManga +import org.jsoup.nodes.Document + +class DisasterScans : Madara("Disaster Scans", "https://disasterscans.com", "en") { + override val popularMangaUrlSelector = "div.post-title a:last-child" + + override fun mangaDetailsParse(document: Document): SManga { + val manga = super.mangaDetailsParse(document) + + with(document) { + select("div.post-title h1").first()?.let { + manga.title = it.ownText() + } + } + + return manga + } +} diff --git a/multisrc/overrides/src/madara/doujinhentai/DoujinHentai.kt b/multisrc/overrides/src/madara/doujinhentai/DoujinHentai.kt new file mode 100644 index 000000000..54977fcfc --- /dev/null +++ b/multisrc/overrides/src/madara/doujinhentai/DoujinHentai.kt @@ -0,0 +1,106 @@ +package eu.kanade.tachiyomi.extension.es.doujinhentai + +import eu.kanade.tachiyomi.annotations.Nsfw +import eu.kanade.tachiyomi.multisrc.madara.Madara +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.SManga +import okhttp3.HttpUrl +import okhttp3.Request +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale + +@Nsfw +class DoujinHentai : Madara("DoujinHentai", "https://doujinhentai.net", "es", SimpleDateFormat("d MMM. yyyy", Locale.ENGLISH)) { + override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/lista-manga-hentai?orderby=views&page=$page", headers) + override fun popularMangaSelector() = "div.col-md-3 a" + override fun popularMangaFromElement(element: Element): SManga { + val manga = SManga.create() + + manga.setUrlWithoutDomain(element.attr("href")) + manga.title = element.select("h5").text() + manga.thumbnail_url = element.select("img").attr("abs:data-src") + + return manga + } + + override fun popularMangaNextPageSelector() = "a[rel=next]" + override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/lista-manga-hentai?orderby=last&page=$page", headers) + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = HttpUrl.parse(baseUrl)!!.newBuilder() + if (query.isNotBlank()) { + url.addPathSegment("search") + url.addQueryParameter("query", query) // query returns results all on one page + } else { + filters.forEach { filter -> + when (filter) { + is GenreSelectFilter -> { + if (filter.state != 0) { + url.addPathSegments("lista-manga-hentai/category/${filter.toUriPart()}") + url.addQueryParameter("page", page.toString()) + } + } + } + } + } + return GET(url.build().toString(), headers) + } + + override fun searchMangaSelector() = "div.c-tabs-item__content > div.c-tabs-item__content, ${popularMangaSelector()}" + override fun searchMangaFromElement(element: Element): SManga { + return if (element.hasAttr("href")) { + popularMangaFromElement(element) // genre search results + } else { + super.searchMangaFromElement(element) // query search results + } + } + + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + override fun chapterListSelector() = "ul.main.version-chap > li.wp-manga-chapter:not(:last-child)" // removing empty li + override val pageListParseSelector = "div#all > img.img-responsive" + override fun getFilterList() = FilterList( + Filter.Header("Solo funciona si la consulta está en blanco"), + GenreSelectFilter() + ) + + class GenreSelectFilter : UriPartFilter( + "Búsqueda de género", + arrayOf( + Pair("", ""), + Pair("Ecchi", "ecchi"), + Pair("Yaoi", "yaoi"), + Pair("Yuri", "yuri"), + Pair("Anal", "anal"), + Pair("Tetonas", "tetonas"), + Pair("Escolares", "escolares"), + Pair("Incesto", "incesto"), + Pair("Virgenes", "virgenes"), + Pair("Masturbacion", "masturbacion"), + Pair("Maduras", "maduras"), + Pair("Lolicon", "lolicon"), + Pair("Bikini", "bikini"), + Pair("Sirvientas", "sirvientas"), + Pair("Enfermera", "enfermera"), + Pair("Embarazada", "embarazada"), + Pair("Ahegao", "ahegao"), + Pair("Casadas", "casadas"), + Pair("Chica Con Pene", "chica-con-pene"), + Pair("Juguetes Sexuales", "juguetes-sexuales"), + Pair("Orgias", "orgias"), + Pair("Harem", "harem"), + Pair("Romance", "romance"), + Pair("Profesores", "profesores"), + Pair("Tentaculos", "tentaculos"), + Pair("Mamadas", "mamadas"), + Pair("Shota", "shota"), + Pair("Interracial", "interracial"), + Pair("Full Color", "full-colo"), + Pair("Sin Censura", "sin-censura"), + Pair("Futanari", "futanari"), + Pair("Doble Penetracion", "doble-penetracion"), + Pair("Cosplay", "cosplay") + ) + ) +} diff --git a/multisrc/overrides/src/madara/doujinyosh/DoujinYosh.kt b/multisrc/overrides/src/madara/doujinyosh/DoujinYosh.kt new file mode 100644 index 000000000..24f8eefe0 --- /dev/null +++ b/multisrc/overrides/src/madara/doujinyosh/DoujinYosh.kt @@ -0,0 +1,96 @@ +package eu.kanade.tachiyomi.extension.id.doujinyosh + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class DoujinYosh : Madara("DoujinYosh", "https://doujinyosh.work", "id") { + // source issue, doing this limits results to one page but not doing it returns no results at all + override fun searchPage(page: Int) = "" + override fun getGenreList() = listOf( + Genre("4 Koma", "4koma"), + Genre("Adult", "adult"), + Genre("Ahegao", "ahegao"), + Genre("Anal", "anal"), + Genre("Animal", "animal"), + Genre("Artist CG", "artist-cg"), + Genre("Big Breast", "big-breast"), + Genre("Big Penis", "big-penis"), + Genre("Bikini", "bikini"), + Genre("Black Mail", "black-mail"), + Genre("Blowjob", "blowjob"), + Genre("Body Swap", "body-swap"), + Genre("Bondage", "bondage"), + Genre("Cheating", "cheating"), + Genre("Crossdressing", "crossdressing"), + Genre("DILF", "dilf"), + Genre("Dark Skin", "dark-skin"), + Genre("Defloration", "defloration"), + Genre("Demon Girl", "demon-girl"), + Genre("Doujin", "doujin"), + Genre("Drugs", "drugs"), + Genre("Drunk", "drunk"), + Genre("Elf", "elf"), + Genre("Famele Only", "famele-only"), + Genre("Femdom", "femdom"), + Genre("Filming", "filming"), + Genre("Footjob", "footjob"), + Genre("Full Color", "full-color"), + Genre("Furry", "furry"), + Genre("Futanari", "futanari"), + Genre("Glasses", "glasses"), + Genre("Gore", "gore"), + Genre("Group", "group"), + Genre("Gyaru", "gyaru"), + Genre("Harem", "harem"), + Genre("Humiliation", "humiliation"), + Genre("Impregnation", "impregnation"), + Genre("Incest", "incest"), + Genre("Inverted Nipples", "inverted-nipples"), + Genre("Kemomimi", "kemomimi"), + Genre("Lactation", "lactation"), + Genre("Loli", "loli"), + Genre("Lolipai", "lolipai"), + Genre("MILF", "milf"), + Genre("Maid", "maid"), + Genre("Male Only", "male-only"), + Genre("Miko", "miko"), + Genre("Mind Break", "mind-break"), + Genre("Mind Control", "mind-control"), + Genre("Monster", "monster"), + Genre("Monster Girl", "monster-girl"), + Genre("Multi-Work Series", "multi-work-series"), + Genre("Nakadashi", "nakadashi"), + Genre("Netorare", "netorare"), + Genre("Otona (R18)", "otona"), + Genre("Oyakodon", "oyakodon"), + Genre("Paizuri", "paizuri"), + Genre("Pantyhose", "pantyhose"), + Genre("Pregnant", "pregnant"), + Genre("Prostitution", "prostitution"), + Genre("Rape", "rape"), + Genre("School Uniform", "school-uniform"), + Genre("Sex Toy", "sex-toy"), + Genre("Shota", "shota"), + Genre("Sister", "sister"), + Genre("Sleep", "sleep"), + Genre("Slime", "slime"), + Genre("Small Breast", "small-breast"), + Genre("Sole Female", "sole-female"), + Genre("Sole Male", "sole-male"), + Genre("Stocking", "stocking"), + Genre("Story Arc", "story-arc"), + Genre("Sweating", "sweating"), + Genre("Swimsuit", "swimsuit"), + Genre("Teacher", "teacher"), + Genre("Tentacles", "tentacles"), + Genre("Tomboy", "tomboy"), + Genre("Tomgirl", "tomgirl"), + Genre("Torture", "torture"), + Genre("Twins", "twins"), + Genre("Virginity", "virginity"), + Genre("Webtoon", "webtoon"), + Genre("XRay", "xray"), + Genre("Yandere", "yandere"), + Genre("Yaoi", "yaoi"), + Genre("Yuri", "yuri") + ) +} diff --git a/multisrc/overrides/src/madara/dropescan/DropeScan.kt b/multisrc/overrides/src/madara/dropescan/DropeScan.kt new file mode 100644 index 000000000..3a2321245 --- /dev/null +++ b/multisrc/overrides/src/madara/dropescan/DropeScan.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.pt.dropescan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import okhttp3.Request + +class DropeScan : Madara("Drope Scan", "https://dropescan.com", "pt-BR") { + 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) +} diff --git a/multisrc/overrides/src/madara/fdmscan/FDMScan.kt b/multisrc/overrides/src/madara/fdmscan/FDMScan.kt new file mode 100644 index 000000000..040da61c8 --- /dev/null +++ b/multisrc/overrides/src/madara/fdmscan/FDMScan.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.pt.fdmscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class FDMScan : Madara("FDM Scan", "https://fdmscan.com", "pt-BR", SimpleDateFormat("MMMM dd, yyyy", Locale("pt", "BR"))) diff --git a/multisrc/overrides/src/madara/firstkissmanga/FirstKissManga.kt b/multisrc/overrides/src/madara/firstkissmanga/FirstKissManga.kt new file mode 100644 index 000000000..e597551a4 --- /dev/null +++ b/multisrc/overrides/src/madara/firstkissmanga/FirstKissManga.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.firstkissmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import okhttp3.Headers +import java.text.SimpleDateFormat +import java.util.Locale + +class FirstKissManga : Madara("1st Kiss", "https://1stkissmanga.com", "en", dateFormat = SimpleDateFormat("dd MMM yyyy", Locale.US)) { + override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl) +} diff --git a/multisrc/overrides/src/madara/firstkissmanhua/FirstKissManhua.kt b/multisrc/overrides/src/madara/firstkissmanhua/FirstKissManhua.kt new file mode 100644 index 000000000..897e5ab54 --- /dev/null +++ b/multisrc/overrides/src/madara/firstkissmanhua/FirstKissManhua.kt @@ -0,0 +1,12 @@ +package eu.kanade.tachiyomi.extension.en.firstkissmanhua + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.Page +import okhttp3.Request +import java.text.SimpleDateFormat +import java.util.Locale + +class FirstKissManhua : Madara("1st Kiss Manhua", "https://1stkissmanhua.com", "en", SimpleDateFormat("d MMM yyyy", Locale.US)) { + override fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headersBuilder().add("Referer", "https://1stkissmanga.com").build()) +} diff --git a/multisrc/overrides/src/madara/furioscans/FurioScans.kt b/multisrc/overrides/src/madara/furioscans/FurioScans.kt new file mode 100644 index 000000000..9517913db --- /dev/null +++ b/multisrc/overrides/src/madara/furioscans/FurioScans.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.pt.furioscans + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class FurioScans : Madara("Furio Scans", "https://furioscans.com", "pt-BR", SimpleDateFormat("dd/MM/yyyy", Locale.getDefault())) diff --git a/multisrc/overrides/src/madara/geceninlordu/GeceninLordu.kt b/multisrc/overrides/src/madara/geceninlordu/GeceninLordu.kt new file mode 100644 index 000000000..900035570 --- /dev/null +++ b/multisrc/overrides/src/madara/geceninlordu/GeceninLordu.kt @@ -0,0 +1,12 @@ +package eu.kanade.tachiyomi.extension.ar.goldenmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.FilterList +import java.text.SimpleDateFormat +import java.util.Locale + + +class GeceninLordu : Madara("Gecenin Lordu", "https://geceninlordu.com/", "tr", SimpleDateFormat("dd MMM yyyy", Locale("tr"))) { + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = GET("$baseUrl/?s=$query&post_type=wp-manga") +} diff --git a/multisrc/overrides/src/madara/goldenmanga/GoldenManga.kt b/multisrc/overrides/src/madara/goldenmanga/GoldenManga.kt new file mode 100644 index 000000000..fe6fb4232 --- /dev/null +++ b/multisrc/overrides/src/madara/goldenmanga/GoldenManga.kt @@ -0,0 +1,28 @@ +package eu.kanade.tachiyomi.extension.ar.goldenmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale + +class GoldenManga : Madara("موقع لترجمة المانجا", "https://golden-manga.com", "ar", SimpleDateFormat("yyyy-MM-dd", Locale.US)) { + override fun searchMangaSelector() = "div.c-image-hover a" + override fun searchMangaFromElement(element: Element): SManga { + return SManga.create().apply { + setUrlWithoutDomain(element.attr("href")) + title = element.attr("title") + thumbnail_url = element.select("img").firstOrNull()?.let { imageFromElement(it) } + } + } + + override fun chapterListSelector() = "div.main a" + override fun chapterFromElement(element: Element): SChapter { + return SChapter.create().apply { + setUrlWithoutDomain(element.attr("href")) + name = element.select("h6:first-of-type").text() + date_upload = parseChapterDate(element.select("h6:last-of-type").firstOrNull()?.ownText()) + } + } +} diff --git a/multisrc/overrides/src/madara/hikariscan/HikariScan.kt b/multisrc/overrides/src/madara/hikariscan/HikariScan.kt new file mode 100644 index 000000000..134f365b6 --- /dev/null +++ b/multisrc/overrides/src/madara/hikariscan/HikariScan.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.pt.hikariscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class HikariScan : Madara("Hikari Scan", "https://hikariscan.com.br", "pt-BR", SimpleDateFormat("dd 'de' MMMM 'de' yyyy", Locale("pt", "BR"))) diff --git a/multisrc/overrides/src/madara/himerafansub/HimeraFansub.kt b/multisrc/overrides/src/madara/himerafansub/HimeraFansub.kt new file mode 100644 index 000000000..72a8a0373 --- /dev/null +++ b/multisrc/overrides/src/madara/himerafansub/HimeraFansub.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.tr.himerafansub + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class HimeraFansub : Madara("Himera Fansub", "https://himera-fansub.com", "tr", SimpleDateFormat("dd MMMM yyyy", Locale.forLanguageTag("tr"))) diff --git a/multisrc/overrides/src/madara/hiperdex/Hiperdex.kt b/multisrc/overrides/src/madara/hiperdex/Hiperdex.kt new file mode 100644 index 000000000..eb7e36a47 --- /dev/null +++ b/multisrc/overrides/src/madara/hiperdex/Hiperdex.kt @@ -0,0 +1,40 @@ +package eu.kanade.tachiyomi.extension.en.hiperdex + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class Hiperdex : Madara("Hiperdex", "https://hiperdex.com", "en") { + override fun getGenreList() = listOf( + Genre("Adult", "adult"), + Genre("Action", "action"), + Genre("Adventure", "adventure"), + Genre("Bully", "bully"), + Genre("Comedy", "comedy"), + Genre("Drama", "drama"), + Genre("Ecchi", "ecchi"), + Genre("Fantasy", "fantasy"), + Genre("Gender Bender", "gender-bender"), + Genre("Harem", "harem"), + Genre("Historical", "historical"), + Genre("Horror", "horror"), + Genre("Isekai", "isekai"), + Genre("Josei", "josei"), + Genre("Martial Arts", "martial-arts"), + Genre("Mature", "mature"), + Genre("Mystery", "mystery"), + Genre("Psychological", "psychological"), + Genre("Romance", "romance"), + Genre("School Life", "school-life"), + Genre("Sci-Fi", "sci-fi"), + Genre("Seinen", "seinen"), + Genre("Shoujo", "shoujo"), + Genre("Shounen", "shounen"), + Genre("Slice of Life", "slice-of-life"), + Genre("Smut", "smut"), + Genre("Sports", "sports"), + Genre("Supernatural", "supernatural"), + Genre("Thriller", "thriller"), + Genre("Tragedy", "tragedy"), + Genre("Yaoi", "yaoi"), + Genre("Yuri", "yuri") + ) +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/Madara.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/Madara.kt new file mode 100644 index 000000000..41fb5dfa3 --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/Madara.kt @@ -0,0 +1,523 @@ +package eu.kanade.tachiyomi.multisrc.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 { + 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) : Filter.Group("Status", status) + private class OrderByFilter : UriPartFilter( + "Order By", + arrayOf( + Pair("