diff --git a/src/en/mangademon/build.gradle b/src/en/mangademon/build.gradle index af1471a58..3150415d0 100644 --- a/src/en/mangademon/build.gradle +++ b/src/en/mangademon/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Manga Demon' pkgNameSuffix = 'en.mangademon' extClass = '.MangaDemon' - extVersionCode = 3 + extVersionCode = 4 } apply from: "$rootDir/common.gradle" diff --git a/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemon.kt b/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemon.kt index dd11f1f27..7e353c6be 100644 --- a/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemon.kt +++ b/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemon.kt @@ -1,8 +1,10 @@ package eu.kanade.tachiyomi.extension.en.mangademon import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga @@ -10,8 +12,10 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request +import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import rx.Observable import java.text.SimpleDateFormat import java.util.Locale @@ -20,10 +24,10 @@ class MangaDemon : ParsedHttpSource() { override val lang = "en" override val supportsLatest = true override val name = "Manga Demon" - override val baseUrl = "https://mangademon.org" + override val baseUrl = "https://manga-demon.org" override val client = network.cloudflareClient.newBuilder() - .rateLimit(2) + .rateLimit(1) .build() override fun headersBuilder() = super.headersBuilder() @@ -38,14 +42,12 @@ class MangaDemon : ParsedHttpSource() { override fun latestUpdatesSelector() = "div.leftside" - override fun latestUpdatesFromElement(element: Element): SManga { - return SManga.create().apply { - element.select("a").apply { - title = attr("title").dropLast(4) - url = attr("href") - } - thumbnail_url = element.select("img").attr("abs:src") + override fun latestUpdatesFromElement(element: Element) = SManga.create().apply { + element.select("a").apply { + title = attr("title") + setUrlWithoutDomain(attr("href")) } + thumbnail_url = element.select("img").attr("abs:src") } // Popular @@ -60,6 +62,44 @@ class MangaDemon : ParsedHttpSource() { override fun popularMangaFromElement(element: Element) = latestUpdatesFromElement(element) // Search + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { + return if (query.isNotEmpty()) { + super.fetchSearchManga(page, query, filters) + } else { + client.newCall(filterSearchRequest(page, filters)) + .asObservableSuccess() + .map(::filterSearchParse) + } + } + + private fun filterSearchRequest(page: Int, filters: FilterList): Request { + val url = "$baseUrl/browse.php".toHttpUrl().newBuilder().apply { + addQueryParameter("list", page.toString()) + filters.forEach { filter -> + when (filter) { + is GenreFilter -> { + filter.checked.forEach { genre -> + addQueryParameter("genre[]", genre) + } + } + is StatusFilter -> { + addQueryParameter("status", filter.selected) + } + is SortFilter -> { + addQueryParameter("orderby", filter.selected) + } + else -> {} + } + } + }.build() + + return GET(url, headers) + } + + private fun filterSearchParse(response: Response) = popularMangaParse(response) + + override fun getFilterList() = getFilters() + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { val url = "$baseUrl/search.php".toHttpUrl().newBuilder() .addQueryParameter("manga", query) @@ -69,18 +109,14 @@ class MangaDemon : ParsedHttpSource() { override fun searchMangaSelector() = "a.boxsizing" - override fun searchMangaFromElement(element: Element): SManga { - return SManga.create().apply { - element.select("a").first().let { - title = element.select("li.boxsizing").text() - url = (element.attr("href")) - val urlsorter = title.replace(":", "%20") - thumbnail_url = ("https://readermc.org/images/thumbnails/$urlsorter.webp") - } - } + override fun searchMangaFromElement(element: Element) = SManga.create().apply { + title = element.text() + setUrlWithoutDomain(element.attr("href")) + val urlSorter = title.replace(":", "%20") + thumbnail_url = ("https://readermc.org/images/thumbnails/$urlSorter.webp") } - override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector() + override fun searchMangaNextPageSelector() = null // Manga details override fun mangaDetailsParse(document: Document): SManga { @@ -109,7 +145,7 @@ class MangaDemon : ParsedHttpSource() { override fun chapterFromElement(element: Element): SChapter { return SChapter.create().apply { element.select("a").let { urlElement -> - url = (urlElement.attr("href")) + setUrlWithoutDomain(urlElement.attr("href")) name = element.select("strong.chapter-title").text() } val date = element.select("time.chapter-update").text() @@ -131,13 +167,13 @@ class MangaDemon : ParsedHttpSource() { } override fun pageListParse(document: Document): List { - val baseImgs = document.select("img.imgholder") + val baseImages = document.select("img.imgholder") .map { it.attr("abs:src") } .toMutableList() - baseImgs.addAll(loadMoreImages(document)) + baseImages.addAll(loadMoreImages(document)) - return baseImgs.mapIndexed { i, img -> Page(i, "", img) } + return baseImages.mapIndexed { i, img -> Page(i, "", img) } } private fun loadMoreImages(document: Document): List { diff --git a/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemonFilters.kt b/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemonFilters.kt new file mode 100644 index 000000000..62a88395f --- /dev/null +++ b/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemonFilters.kt @@ -0,0 +1,93 @@ +package eu.kanade.tachiyomi.extension.en.mangademon + +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList + +abstract class SelectFilter( + name: String, + private val options: List>, +) : Filter.Select( + name, + options.map { it.first }.toTypedArray(), +) { + val selected get() = options[state].second +} + +class StatusFilter : SelectFilter("Status", status) { + companion object { + private val status = listOf( + Pair("All", "all"), + Pair("Ongoing", "ongoing"), + Pair("Completed", "completed"), + ) + } +} + +class SortFilter : SelectFilter("Sort", sort) { + companion object { + private val sort = listOf( + Pair("Top Views", "VIEWS DESC"), + Pair("A To Z", "NAME ASC"), + ) + } +} + +class CheckBoxFilter( + name: String, + val value: String, +) : Filter.CheckBox(name) + +class GenreFilter : Filter.Group( + "Genre", + genres.map { CheckBoxFilter(it.first, it.second) }, +) { + val checked get() = state.filter { it.state }.map { it.value } + + companion object { + private val genres = listOf( + Pair("All", "all"), + Pair("Action", "1"), + Pair("Adventure", "2"), + Pair("Comedy", "3"), + Pair("Cooking", "34"), + Pair("Doujinshi", "25"), + Pair("Drama", "4"), + Pair("Ecchi", "19"), + Pair("Fantasy", "5"), + Pair("Gender Bender", "30"), + Pair("Harem", "10"), + Pair("Historical", "28"), + Pair("Horror", "8"), + Pair("Isekai", "33"), + Pair("Josei", "31"), + Pair("Martial Arts", "6"), + Pair("Mature", "22"), + Pair("Mecha", "32"), + Pair("Mystery", "15"), + Pair("One Shot", "26"), + Pair("Psychological", "11"), + Pair("Romance", "12"), + Pair("School Life", "13"), + Pair("Sci-fi", "16"), + Pair("Seinen", "17"), + Pair("Shoujo", "14"), + Pair("Shoujo Ai", "23"), + Pair("Shounen", "7"), + Pair("Shounen Ai", "29"), + Pair("Slice of Life", "21"), + Pair("Smut", "27"), + Pair("Sports", "20"), + Pair("Supernatural", "9"), + Pair("Tragedy", "18"), + Pair("Webtoons", "24"), + ) + } +} + +fun getFilters() = FilterList( + Filter.Header("Ignored when using text search"), + Filter.Separator(), + SortFilter(), + StatusFilter(), + GenreFilter(), +)