diff --git a/multisrc/overrides/madara/comickiba/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/comickiba/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index e56790e8f..000000000 Binary files a/multisrc/overrides/madara/comickiba/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/comickiba/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/comickiba/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 8b26a2517..000000000 Binary files a/multisrc/overrides/madara/comickiba/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/comickiba/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/comickiba/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 01527e9cc..000000000 Binary files a/multisrc/overrides/madara/comickiba/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/comickiba/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/comickiba/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index e146ddafd..000000000 Binary files a/multisrc/overrides/madara/comickiba/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/comickiba/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/comickiba/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 6209ec8da..000000000 Binary files a/multisrc/overrides/madara/comickiba/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/comickiba/res/web_hi_res_512.png b/multisrc/overrides/madara/comickiba/res/web_hi_res_512.png deleted file mode 100644 index 95e27c4c2..000000000 Binary files a/multisrc/overrides/madara/comickiba/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/comickiba/src/ComicKiba.kt b/multisrc/overrides/madara/comickiba/src/ComicKiba.kt deleted file mode 100644 index c1d13e737..000000000 --- a/multisrc/overrides/madara/comickiba/src/ComicKiba.kt +++ /dev/null @@ -1,7 +0,0 @@ -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/mangareader/comickiba/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangareader/comickiba/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..06d80b926 Binary files /dev/null and b/multisrc/overrides/mangareader/comickiba/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangareader/comickiba/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangareader/comickiba/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..628eef8f3 Binary files /dev/null and b/multisrc/overrides/mangareader/comickiba/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangareader/comickiba/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangareader/comickiba/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..5bbe93d59 Binary files /dev/null and b/multisrc/overrides/mangareader/comickiba/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangareader/comickiba/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangareader/comickiba/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..9cff69862 Binary files /dev/null and b/multisrc/overrides/mangareader/comickiba/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangareader/comickiba/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangareader/comickiba/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..36f1e33ac Binary files /dev/null and b/multisrc/overrides/mangareader/comickiba/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangareader/comickiba/res/web_hi_res_512.png b/multisrc/overrides/mangareader/comickiba/res/web_hi_res_512.png new file mode 100644 index 000000000..717e1de04 Binary files /dev/null and b/multisrc/overrides/mangareader/comickiba/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangareader/comickiba/src/Manhuagold.kt b/multisrc/overrides/mangareader/comickiba/src/Manhuagold.kt new file mode 100644 index 000000000..de17ec25b --- /dev/null +++ b/multisrc/overrides/mangareader/comickiba/src/Manhuagold.kt @@ -0,0 +1,233 @@ +package eu.kanade.tachiyomi.extension.en.comickiba + +import eu.kanade.tachiyomi.multisrc.mangareader.MangaReader +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.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import org.jsoup.nodes.TextNode +import org.jsoup.select.Evaluator +import rx.Observable + +class Manhuagold : MangaReader() { + + override val name = "Manhuagold" + + override val lang = "en" + + override val baseUrl = "https://manhuagold.com" + + override val client = network.cloudflareClient.newBuilder() + .rateLimit(2) + .build() + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "$baseUrl/") + + // Popular + + override fun popularMangaRequest(page: Int) = + GET("$baseUrl/filter/$page/?sort=views&sex=All&chapter_count=0", headers) + + // Latest + + override fun latestUpdatesRequest(page: Int) = + GET("$baseUrl/filter/$page/?sort=latest-updated&sex=All&chapter_count=0", headers) + + // Search + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val urlBuilder = baseUrl.toHttpUrl().newBuilder() + if (query.isNotBlank()) { + urlBuilder.addPathSegment("search").apply { + addQueryParameter("keyword", query) + } + } else { + urlBuilder.addPathSegment("filter").apply { + filters.ifEmpty(::getFilterList).forEach { filter -> + when (filter) { + is Select -> { + addQueryParameter(filter.param, filter.selection) + } + is GenresFilter -> { + addQueryParameter(filter.param, filter.selection) + } + else -> {} + } + } + } + } + + urlBuilder.addPathSegment(page.toString()) + urlBuilder.addPathSegment("") + + return GET(urlBuilder.build(), headers) + } + + override fun searchMangaSelector() = ".manga_list-sbs .manga-poster" + + override fun searchMangaFromElement(element: Element) = + SManga.create().apply { + setUrlWithoutDomain(element.attr("href")) + element.selectFirst(Evaluator.Tag("img"))!!.let { + title = it.attr("alt") + thumbnail_url = it.imgAttr() + } + } + + override fun searchMangaNextPageSelector() = "ul.pagination > li.active + li" + + // Filters + + override fun getFilterList() = + FilterList( + Note, + StatusFilter(), + SortFilter(), + GenresFilter(), + ) + + // Details + + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + val root = document.selectFirst(Evaluator.Id("ani_detail"))!! + val mangaTitle = root.selectFirst(Evaluator.Class("manga-name"))!!.ownText() + title = mangaTitle + description = root.run { + val description = selectFirst(Evaluator.Class("description"))!!.ownText() + when (val altTitle = selectFirst(Evaluator.Class("manga-name-or"))!!.ownText()) { + "", mangaTitle -> description + else -> "$description\n\nAlternative Title: $altTitle" + } + } + thumbnail_url = root.selectFirst(Evaluator.Tag("img"))!!.imgAttr() + genre = root.selectFirst(Evaluator.Class("genres"))!!.children().joinToString { it.ownText() } + for (item in root.selectFirst(Evaluator.Class("anisc-info"))!!.children()) { + if (item.hasClass("item").not()) continue + when (item.selectFirst(Evaluator.Class("item-head"))!!.ownText()) { + "Authors:" -> item.parseAuthorsTo(this) + "Status:" -> status = when (item.selectFirst(Evaluator.Class("name"))!!.ownText().lowercase()) { + "ongoing" -> SManga.ONGOING + "completed" -> SManga.COMPLETED + "on-hold" -> SManga.ON_HIATUS + "canceled" -> SManga.CANCELLED + else -> SManga.UNKNOWN + } + } + } + } + + private fun Element.parseAuthorsTo(manga: SManga) { + val authors = select(Evaluator.Tag("a")) + val text = authors.map { it.ownText().replace(",", "") } + val count = authors.size + when (count) { + 0 -> return + 1 -> { + manga.author = text[0] + return + } + } + val authorList = ArrayList(count) + val artistList = ArrayList(count) + for ((index, author) in authors.withIndex()) { + val textNode = author.nextSibling() as? TextNode + val list = if (textNode != null && "(Art)" in textNode.wholeText) artistList else authorList + list.add(text[index]) + } + if (authorList.isEmpty().not()) manga.author = authorList.joinToString() + if (artistList.isEmpty().not()) manga.artist = artistList.joinToString() + } + + // Chapters + + override fun chapterListRequest(mangaUrl: String, type: String): Request = + GET(baseUrl + mangaUrl, headers) + + override fun parseChapterElements(response: Response, isVolume: Boolean): List { + TODO("Not yet implemented") + } + + override val chapterType = "" + override val volumeType = "" + + override fun fetchChapterList(manga: SManga): Observable> { + return client.newCall(chapterListRequest(manga)) + .asObservableSuccess() + .map(::parseChapterList) + } + + private fun parseChapterList(response: Response): List { + val document = response.use { it.asJsoup() } + + return document.select(chapterListSelector()) + .map(::chapterFromElement) + } + + private fun chapterListSelector(): String = "#chapters-list > li" + + private fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { + element.selectFirst("a")!!.run { + setUrlWithoutDomain(attr("href")) + name = selectFirst(".name")?.text() ?: text() + } + } + + // Images + + override fun fetchPageList(chapter: SChapter): Observable> = Observable.fromCallable { + val document = client.newCall(pageListRequest(chapter)).execute().asJsoup() + + val script = document.selectFirst("script:containsData(const CHAPTER_ID)")!!.data() + val id = script.substringAfter("const CHAPTER_ID = ").substringBefore(";") + + val ajaxHeaders = super.headersBuilder().apply { + add("Accept", "application/json, text/javascript, */*; q=0.01") + add("Referer", baseUrl + chapter.url) + add("X-Requested-With", "XMLHttpRequest") + }.build() + + val ajaxUrl = "$baseUrl/ajax/image/list/chap/$id" + client.newCall(GET(ajaxUrl, ajaxHeaders)).execute().let(::pageListParse) + } + + override fun pageListParse(response: Response): List { + val document = response.use { it.parseHtmlProperty() } + + val pageList = document.select("div").map { + val index = it.attr("data-number").toInt() + val imgUrl = it.imgAttr().ifEmpty { it.selectFirst("img")!!.imgAttr() } + + Page(index, "", imgUrl) + } + + return pageList + } + + // Utilities + + // From mangathemesia + private fun Element.imgAttr(): String = when { + hasAttr("data-lazy-src") -> attr("abs:data-lazy-src") + hasAttr("data-src") -> attr("abs:data-src") + else -> attr("abs:src") + } + + private fun Response.parseHtmlProperty(): Document { + val html = Json.parseToJsonElement(body.string()).jsonObject["html"]!!.jsonPrimitive.content + return Jsoup.parseBodyFragment(html) + } +} diff --git a/multisrc/overrides/mangareader/comickiba/src/ManhuagoldFilters.kt b/multisrc/overrides/mangareader/comickiba/src/ManhuagoldFilters.kt new file mode 100644 index 000000000..f8dccbc81 --- /dev/null +++ b/multisrc/overrides/mangareader/comickiba/src/ManhuagoldFilters.kt @@ -0,0 +1,142 @@ +package eu.kanade.tachiyomi.extension.en.comickiba + +import eu.kanade.tachiyomi.source.model.Filter + +object Note : Filter.Header("NOTE: Ignored if using text search!") + +sealed class Select( + name: String, + val param: String, + values: Array, +) : Filter.Select(name, values) { + open val selection: String + get() = if (state == 0) "" else state.toString() +} + +class StatusFilter( + values: Array = statuses.keys.toTypedArray(), +) : Select("Status", "status", values) { + override val selection: String + get() = statuses[values[state]]!! + + companion object { + private val statuses = mapOf( + "All" to "", + "Completed" to "completed", + "OnGoing" to "on-going", + "On-Hold" to "on-hold", + "Canceled" to "canceled", + ) + } +} + +class SortFilter( + values: Array = orders.keys.toTypedArray(), +) : Select("Sort", "sort", values) { + override val selection: String + get() = orders[values[state]]!! + + companion object { + private val orders = mapOf( + "Default" to "default", + "Latest Updated" to "latest-updated", + "Most Viewed" to "views", + "Most Viewed Month" to "views_month", + "Most Viewed Week" to "views_week", + "Most Viewed Day" to "views_day", + "Score" to "score", + "Name A-Z" to "az", + "Name Z-A" to "za", + "The highest chapter count" to "chapters", + "Newest" to "new", + "Oldest" to "old", + ) + } +} + +class Genre(name: String, val id: String) : Filter.CheckBox(name) + +class GenresFilter( + values: List = genres, +) : Filter.Group("Genres", values) { + val param = "genres" + + val selection: String + get() = state.filter { it.state }.joinToString(",") { it.id } + + companion object { + private val genres: List + get() = listOf( + Genre("Action", "37"), + Genre("Adaptation", "19"), + Genre("Adult", "5310"), + Genre("Adventure", "38"), + Genre("Aliens", "5436"), + Genre("Animals", "1552"), + Genre("Award Winning", "39"), + Genre("Comedy", "202"), + Genre("Comic", "287"), + Genre("Cooking", "277"), + Genre("Crime", "2723"), + Genre("Delinquents", "4438"), + Genre("Demons", "379"), + Genre("Drama", "3"), + Genre("Ecchi", "17"), + Genre("Fantasy", "197"), + Genre("Full Color", "13"), + Genre("Gender Bender", "221"), + Genre("Genderswap", "2290"), + Genre("Ghosts", "2866"), + Genre("Gore", "42"), + Genre("Harem", "222"), + Genre("Historical", "4"), + Genre("Horror", "5"), + Genre("Isekai", "259"), + Genre("Josei", "292"), + Genre("Loli", "5449"), + Genre("Long Strip", "7"), + Genre("Magic", "272"), + Genre("Manhwa", "266"), + Genre("Martial Arts", "40"), + Genre("Mature", "5311"), + Genre("Mecha", "2830"), + Genre("Medical", "1598"), + Genre("Military", "43"), + Genre("Monster Girls", "2307"), + Genre("Monsters", "298"), + Genre("Music", "3182"), + Genre("Mystery", "6"), + Genre("Office Workers", "14"), + Genre("Official Colored", "1046"), + Genre("Philosophical", "2776"), + Genre("Post-Apocalyptic", "1059"), + Genre("Psychological", "493"), + Genre("Reincarnation", "204"), + Genre("Reverse", "280"), + Genre("Reverse Harem", "199"), + Genre("Romance", "186"), + Genre("School Life", "601"), + Genre("Sci-Fi", "1845"), + Genre("Sexual Violence", "731"), + Genre("Shoujo", "254"), + Genre("Slice of Life", "10"), + Genre("Sports", "4066"), + Genre("Superhero", "481"), + Genre("Supernatural", "198"), + Genre("Survival", "44"), + Genre("Thriller", "1058"), + Genre("Time Travel", "299"), + Genre("Tragedy", "41"), + Genre("Video Games", "1846"), + Genre("Villainess", "278"), + Genre("Virtual Reality", "1847"), + Genre("Web Comic", "12"), + Genre("Webtoon", "279"), + Genre("Webtoons", "267"), + Genre("Wuxia", "203"), + Genre("Yaoi", "18"), + Genre("Yuri", "11"), + Genre("Zombies", "1060"), + ) + } +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt index 6ad5abc1b..6124edd3e 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt @@ -73,7 +73,6 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("CoffeeManga.top (unoriginal)", "https://coffeemanga.top", "en", isNsfw = true, className = "CoffeeMangaTop"), SingleLang("Colored Manga", "https://coloredmanga.com", "en", overrideVersionCode = 2), SingleLang("Comic Scans", "https://www.comicscans.org", "en", isNsfw = false), - SingleLang("ComicKiba", "https://comickiba.com", "en", overrideVersionCode = 1), SingleLang("Comics Valley", "https://comicsvalley.com", "hi", isNsfw = true, overrideVersionCode = 1), SingleLang("ComicsWorld", "https://comicsworld.in", "hi"), SingleLang("Comicz.net v2", "https://v2.comiz.net", "all", isNsfw = true, className = "ComiczNetV2"), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangareader/MangaReader.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangareader/MangaReader.kt index 8cc3831c0..2abfe6071 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangareader/MangaReader.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangareader/MangaReader.kt @@ -73,7 +73,7 @@ abstract class MangaReader : HttpSource(), ConfigurableSource { open fun updateChapterList(manga: SManga, chapters: List) = Unit - final override fun fetchChapterList(manga: SManga): Observable> = Observable.fromCallable { + override fun fetchChapterList(manga: SManga): Observable> = Observable.fromCallable { val path = manga.url val isVolume = path.endsWith(VOLUME_URL_SUFFIX) val type = if (isVolume) volumeType else chapterType diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangareader/MangaReaderGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangareader/MangaReaderGenerator.kt index bacbb16fa..eaee555b4 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangareader/MangaReaderGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangareader/MangaReaderGenerator.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.multisrc.mangareader import generator.ThemeSourceData.MultiLang +import generator.ThemeSourceData.SingleLang import generator.ThemeSourceGenerator class MangaReaderGenerator : ThemeSourceGenerator { @@ -23,6 +24,15 @@ class MangaReaderGenerator : ThemeSourceGenerator { isNsfw = true, overrideVersionCode = 3, ), + SingleLang( + name = "Manhuagold", + baseUrl = "https://manhuagold.com", + lang = "en", + isNsfw = true, + className = "Manhuagold", + pkgName = "comickiba", + overrideVersionCode = 33, + ), ) companion object {