From 2cb370f0612a86b1103c55d897969bcbd3c230cd Mon Sep 17 00:00:00 2001 From: Mike <51273546+SnakeDoc83@users.noreply.github.com> Date: Fri, 24 Apr 2020 21:19:31 -0400 Subject: [PATCH] MangaBox - add Mangakakalots (#2810) MangaBox - add Mangakakalots --- src/all/mangabox/build.gradle | 2 +- .../extension/all/mangabox/MangaBox.kt | 118 +++++++++--------- .../extension/all/mangabox/MangaBoxFactory.kt | 114 +++++++++-------- 3 files changed, 119 insertions(+), 115 deletions(-) diff --git a/src/all/mangabox/build.gradle b/src/all/mangabox/build.gradle index 1c5cec4ea..fb66f927e 100644 --- a/src/all/mangabox/build.gradle +++ b/src/all/mangabox/build.gradle @@ -5,7 +5,7 @@ ext { appName = 'Tachiyomi: MangaBox (Mangakakalot and others)' pkgNameSuffix = 'all.mangabox' extClass = '.MangaBoxFactory' - extVersionCode = 18 + extVersionCode = 19 libVersion = '1.2' } diff --git a/src/all/mangabox/src/eu/kanade/tachiyomi/extension/all/mangabox/MangaBox.kt b/src/all/mangabox/src/eu/kanade/tachiyomi/extension/all/mangabox/MangaBox.kt index 4b7826d34..a43fbd934 100644 --- a/src/all/mangabox/src/eu/kanade/tachiyomi/extension/all/mangabox/MangaBox.kt +++ b/src/all/mangabox/src/eu/kanade/tachiyomi/extension/all/mangabox/MangaBox.kt @@ -118,14 +118,9 @@ abstract class MangaBox ( title = infoElement.select("h1, h2").first().text() author = infoElement.select("li:contains(author) a, td:containsOwn(author) + td").text() status = parseStatus(infoElement.select("li:contains(status), td:containsOwn(status) + td").text()) - genre = infoElement.select("div.manga-info-top li:contains(genres)").let { kakalotE -> - if (kakalotE.isNotEmpty()) { - kakalotE.text().substringAfter(": ") - } else { - // Nelo - infoElement.select("td:containsOwn(genres) + td a").joinToString { it.text() } - } - } + genre = infoElement.select("div.manga-info-top li:contains(genres)").firstOrNull() + ?.select("a")?.joinToString { it.text() } // kakalot + ?: infoElement.select("td:containsOwn(genres) + td a").joinToString { it.text() } // nelo description = document.select(descriptionSelector)?.first()?.ownText() ?.replace("""^$title summary:\s""".toRegex(), "") ?.replace("""<\s*br\s*\/?>""".toRegex(), "\n") @@ -155,6 +150,7 @@ abstract class MangaBox ( element.select("a").let { url = it.attr("abs:href").substringAfter(baseUrl) // intentionally not using setUrlWithoutDomain name = it.text() + scanlator = HttpUrl.parse(it.attr("abs:href"))!!.host() // show where chapters are actually from } date_upload = parseChapterDate(element.select("span").last().text(), element.ownerDocument().location()) ?: 0 } @@ -228,8 +224,8 @@ abstract class MangaBox ( Filter.Header("NOTE: Ignored if using text search!"), Filter.Separator(), SortFilter(), - StatusFilter(), - GenreFilter() + StatusFilter(getStatusPairs()), + GenreFilter(getGenrePairs()) ) private class SortFilter : UriPartFilter("Sort", arrayOf( @@ -238,56 +234,60 @@ abstract class MangaBox ( Pair("topview", "Top read") )) - private class StatusFilter : UriPartFilter("Status", arrayOf( - Pair("all", "ALL"), - Pair("completed", "Completed"), - Pair("ongoing", "Ongoing"), - Pair("drop", "Dropped") - )) + open class StatusFilter(val statusPairs: Array>) : UriPartFilter("Status", statusPairs) - private class GenreFilter : UriPartFilter("Category", arrayOf( - Pair("all", "ALL"), - Pair("2", "Action"), - Pair("3", "Adult"), - Pair("4", "Adventure"), - Pair("6", "Comedy"), - Pair("7", "Cooking"), - Pair("9", "Doujinshi"), - Pair("10", "Drama"), - Pair("11", "Ecchi"), - Pair("12", "Fantasy"), - Pair("13", "Gender bender"), - Pair("14", "Harem"), - Pair("15", "Historical"), - Pair("16", "Horror"), - Pair("45", "Isekai"), - Pair("17", "Josei"), - Pair("44", "Manhua"), - Pair("43", "Manhwa"), - Pair("19", "Martial arts"), - Pair("20", "Mature"), - Pair("21", "Mecha"), - Pair("22", "Medical"), - Pair("24", "Mystery"), - Pair("25", "One shot"), - Pair("26", "Psychological"), - Pair("27", "Romance"), - Pair("28", "School life"), - Pair("29", "Sci fi"), - Pair("30", "Seinen"), - Pair("31", "Shoujo"), - Pair("32", "Shoujo ai"), - Pair("33", "Shounen"), - Pair("34", "Shounen ai"), - Pair("35", "Slice of life"), - Pair("36", "Smut"), - Pair("37", "Sports"), - Pair("38", "Supernatural"), - Pair("39", "Tragedy"), - Pair("40", "Webtoons"), - Pair("41", "Yaoi"), - Pair("42", "Yuri") - )) + open fun getStatusPairs() = arrayOf( + Pair("all", "ALL"), + Pair("completed", "Completed"), + Pair("ongoing", "Ongoing"), + Pair("drop", "Dropped") + ) + + private class GenreFilter(val genrePairs: Array>) : UriPartFilter("Category", genrePairs) + + open fun getGenrePairs(): Array> = arrayOf( + Pair("all", "ALL"), + Pair("2", "Action"), + Pair("3", "Adult"), + Pair("4", "Adventure"), + Pair("6", "Comedy"), + Pair("7", "Cooking"), + Pair("9", "Doujinshi"), + Pair("10", "Drama"), + Pair("11", "Ecchi"), + Pair("12", "Fantasy"), + Pair("13", "Gender bender"), + Pair("14", "Harem"), + Pair("15", "Historical"), + Pair("16", "Horror"), + Pair("45", "Isekai"), + Pair("17", "Josei"), + Pair("44", "Manhua"), + Pair("43", "Manhwa"), + Pair("19", "Martial arts"), + Pair("20", "Mature"), + Pair("21", "Mecha"), + Pair("22", "Medical"), + Pair("24", "Mystery"), + Pair("25", "One shot"), + Pair("26", "Psychological"), + Pair("27", "Romance"), + Pair("28", "School life"), + Pair("29", "Sci fi"), + Pair("30", "Seinen"), + Pair("31", "Shoujo"), + Pair("32", "Shoujo ai"), + Pair("33", "Shounen"), + Pair("34", "Shounen ai"), + Pair("35", "Slice of life"), + Pair("36", "Smut"), + Pair("37", "Sports"), + Pair("38", "Supernatural"), + Pair("39", "Tragedy"), + Pair("40", "Webtoons"), + Pair("41", "Yaoi"), + Pair("42", "Yuri") + ) open class UriPartFilter(displayName: String, val vals: Array>) : Filter.Select(displayName, vals.map { it.second }.toTypedArray()) { diff --git a/src/all/mangabox/src/eu/kanade/tachiyomi/extension/all/mangabox/MangaBoxFactory.kt b/src/all/mangabox/src/eu/kanade/tachiyomi/extension/all/mangabox/MangaBoxFactory.kt index 173508213..0799af6e4 100644 --- a/src/all/mangabox/src/eu/kanade/tachiyomi/extension/all/mangabox/MangaBoxFactory.kt +++ b/src/all/mangabox/src/eu/kanade/tachiyomi/extension/all/mangabox/MangaBoxFactory.kt @@ -1,19 +1,14 @@ package eu.kanade.tachiyomi.extension.all.mangabox 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.MangasPage -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.util.asJsoup import okhttp3.Request -import okhttp3.RequestBody import okhttp3.Response -import org.jsoup.nodes.Element import java.text.SimpleDateFormat import java.util.Locale @@ -22,8 +17,8 @@ class MangaBoxFactory : SourceFactory { Mangakakalot(), Manganelo(), Mangabat(), - MangaOnl() - //ChapterManga() + MangaOnl(), + OtherMangakakalot() ) } @@ -45,7 +40,7 @@ abstract class MangaBoxPathedGenres( ) class GenreFilter(genrePairs: Array>) : UriPartFilter("Category", genrePairs) // Pair("path_segment/", "display name") - abstract fun getGenrePairs(): Array> + abstract override fun getGenrePairs(): Array> override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { return if (query.isNotBlank()) { GET("$baseUrl/$simpleQueryPath${normalizeSearchQuery(query)}?page=$page", headers) @@ -180,54 +175,63 @@ class MangaOnl : MangaBoxPathedGenres("MangaOnl", "https://mangaonl.com", "en") ) } -class ChapterManga : MangaBox("ChapterManga", "https://chaptermanga.com", "en", SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH)) { - override val popularUrlPath = "hot-manga-page-" - override val latestUrlPath = "read-latest-manga-page-" - override fun chapterListRequest(manga: SManga): Request { - val response = client.newCall(GET(baseUrl + manga.url, headers)).execute() - val cookie = response.headers("set-cookie") - .filter{ it.contains("laravel_session") } - .map{ it.substringAfter("=").substringBefore(";") } - val document = response.asJsoup() - val token = document.select("meta[name=\"csrf-token\"]").attr("content") - val script = document.select("script:containsData(manga_slug)").first() - val mangaSlug = script.data().substringAfter("manga_slug : \'").substringBefore("\'") - val mangaId = script.data().substringAfter("manga_id : \'").substringBefore("\'") - val tokenHeaders = headers.newBuilder() - .add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") - .add("X-CSRF-Token", token) - .add("Cookie", cookie.toString()) - .build() - val body = RequestBody.create(null, "manga_slug=$mangaSlug&manga_id=$mangaId") - - return POST("$baseUrl/get-chapter-list", tokenHeaders, body) - } - override fun chapterListSelector() = "div.row" - override fun chapterFromElement(element: Element): SChapter = super.chapterFromElement(element).apply { - chapter_number = Regex("""[Cc]hapter\s\d*""").find(name)?.value?.substringAfter(" ")?.toFloatOrNull() ?: 0F - } - // TODO chapterlistparse -- default chapter order could be better - override fun getFilterList() = FilterList() - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val site = baseUrl.substringAfter("//") - val searchHeaders = headers.newBuilder().add("Content-Type", "application/x-www-form-urlencoded").build() - val body = RequestBody.create(null, "q=site%3A$site+inurl%3A$site%2Fread-manga+${query.replace(" ", "+")}&b=&kl=us-en") - - return POST("https://duckduckgo.com/html/", searchHeaders, body) - } +class OtherMangakakalot : MangaBox("Mangakakalots (unoriginal)", "https://mangakakalots.com", "en") { + override fun searchMangaSelector(): String = "${super.searchMangaSelector()}, div.list-truyen-item-wrap" override fun searchMangaParse(response: Response): MangasPage { - val mangas = response.asJsoup().select(searchMangaSelector()) - .filter{ it.text().startsWith("Read") } - .map{ searchMangaFromElement(it) } + val document = response.asJsoup() + val mangas = document.select(searchMangaSelector()).map { searchMangaFromElement(it) } + val hasNextPage = !response.request().url().toString() + .contains(document.select(searchMangaNextPageSelector()).attr("href")) - return MangasPage(mangas, false) - } - override fun searchMangaSelector() = "div.result h2 a" - override fun searchMangaFromElement(element: Element): SManga { - return SManga.create().apply { - title = element.text().substringAfter("Read").substringBeforeLast("online").trim() - setUrlWithoutDomain(element.attr("href")) - } + return MangasPage(mangas, hasNextPage) } + override fun searchMangaNextPageSelector() = "div.group_page a:last-of-type" + override fun getStatusPairs() = arrayOf( + Pair("all", "ALL"), + Pair("Completed", "Completed"), + Pair("Ongoing", "Ongoing") + ) + override fun getGenrePairs() = arrayOf( + Pair("all", "ALL"), + Pair("Action", "Action"), + Pair("Adult", "Adult"), + Pair("Adventure", "Adventure"), + Pair("Comedy", "Comedy"), + Pair("Cooking", "Cooking"), + Pair("Doujinshi", "Doujinshi"), + Pair("Drama", "Drama"), + Pair("Ecchi", "Ecchi"), + Pair("Fantasy", "Fantasy"), + Pair("Gender bender", "Gender bender"), + Pair("Harem", "Harem"), + Pair("Historical", "Historical"), + Pair("Horror", "Horror"), + Pair("Isekai", "Isekai"), + Pair("Josei", "Josei"), + Pair("Manhua", "Manhua"), + Pair("Manhwa", "Manhwa"), + Pair("Martial arts", "Martial arts"), + Pair("Mature", "Mature"), + Pair("Mecha", "Mecha"), + Pair("Medical", "Medical"), + Pair("Mystery", "Mystery"), + Pair("One shot", "One shot"), + Pair("Psychological", "Psychological"), + Pair("Romance", "Romance"), + Pair("School life", "School life"), + Pair("Sci fi", "Sci fi"), + Pair("Seinen", "Seinen"), + Pair("Shoujo", "Shoujo"), + Pair("Shoujo ai", "Shoujo ai"), + Pair("Shounen", "Shounen"), + Pair("Shounen ai", "Shounen ai"), + Pair("Slice of life", "Slice of life"), + Pair("Smut", "Smut"), + Pair("Sports", "Sports"), + Pair("Supernatural", "Supernatural"), + Pair("Tragedy", "Tragedy"), + Pair("Webtoons", "Webtoons"), + Pair("Yaoi", "Yaoi"), + Pair("Yuri", "Yuri") + ) } -