From e50ade5026a5e3f439790d89cdaf024e0b014047 Mon Sep 17 00:00:00 2001 From: Davide <49226282+pizidavi@users.noreply.github.com> Date: Mon, 7 Mar 2022 14:11:04 +0100 Subject: [PATCH] Manga-Raw.club: Refactor (#11036) Improved chapter title Added alternative title in manga description --- src/en/mangarawclub/build.gradle | 2 +- .../extension/en/mangarawclub/MangaRawClub.kt | 255 +++++++----------- 2 files changed, 93 insertions(+), 164 deletions(-) diff --git a/src/en/mangarawclub/build.gradle b/src/en/mangarawclub/build.gradle index 33055d8e0..51049017d 100644 --- a/src/en/mangarawclub/build.gradle +++ b/src/en/mangarawclub/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'manga-raw.club' pkgNameSuffix = 'en.mangarawclub' extClass = '.MangaRawClub' - extVersionCode = 6 + extVersionCode = 7 isNsfw = true } diff --git a/src/en/mangarawclub/src/eu/kanade/tachiyomi/extension/en/mangarawclub/MangaRawClub.kt b/src/en/mangarawclub/src/eu/kanade/tachiyomi/extension/en/mangarawclub/MangaRawClub.kt index 539897035..17a38846c 100644 --- a/src/en/mangarawclub/src/eu/kanade/tachiyomi/extension/en/mangarawclub/MangaRawClub.kt +++ b/src/en/mangarawclub/src/eu/kanade/tachiyomi/extension/en/mangarawclub/MangaRawClub.kt @@ -14,7 +14,6 @@ import okhttp3.FormBody import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element import rx.Observable @@ -34,6 +33,10 @@ class MangaRawClub : ParsedHttpSource() { .readTimeout(30, TimeUnit.SECONDS) .build() + companion object { + const val altName = "Alternative Name: " + } + override fun popularMangaRequest(page: Int): Request { return GET("$baseUrl/browse/?results=$page&filter=views", headers) } @@ -42,60 +45,56 @@ class MangaRawClub : ParsedHttpSource() { return GET("$baseUrl/listy/manga/?results=$page", headers) } - override fun popularMangaSelector() = "ul.novel-list.grid.col.col2 > li" + override fun searchMangaSelector() = "ul.novel-list > li.novel-item" + override fun popularMangaSelector() = searchMangaSelector() + override fun latestUpdatesSelector() = searchMangaSelector() - // override fun popularMangaSelector() = "li.novel-item" - override fun latestUpdatesSelector() = popularMangaSelector() - override fun searchMangaSelector() = "ul.novel-list.grid.col.col1 > li" - - override fun popularMangaFromElement(element: Element): SManga { + override fun searchMangaFromElement(element: Element): SManga { val manga = SManga.create() - val coverElement = element.getElementsByClass("novel-cover").first() - var titleElement = element.getElementsByClass("novel-title text1row").first() - if (titleElement == null) { - titleElement = element.getElementsByClass("novel-title text2row").first() - } - manga.thumbnail_url = coverElement.select("img").attr("abs:data-src") + manga.title = element.select(".novel-title").first()?.text() ?: "" + manga.thumbnail_url = element.select(".novel-cover img").attr("abs:data-src") manga.setUrlWithoutDomain(element.select("a").first().attr("href")) - manga.title = titleElement.text() return manga } + override fun popularMangaFromElement(element: Element): SManga = searchMangaFromElement(element) + override fun latestUpdatesFromElement(element: Element): SManga = searchMangaFromElement(element) - override fun latestUpdatesFromElement(element: Element): SManga = - popularMangaFromElement(element) - - override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element) - - override fun popularMangaNextPageSelector() = "ul.pagination > li:last-child > a" - override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() - override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + override fun searchMangaNextPageSelector() = "ul.pagination > li:last-child > a" + override fun popularMangaNextPageSelector() = searchMangaNextPageSelector() + override fun latestUpdatesNextPageSelector() = searchMangaNextPageSelector() override fun mangaDetailsParse(document: Document): SManga { val manga = SManga.create() - val authorElement = document.getElementsByClass("author").first() - manga.author = authorElement.select("a").attr("title") - // manga.author = document.select("a[class=\"property-item\"]").first().attr("title") - manga.artist = "" + val author = document.select(".author a").first()?.attr("title") ?: "" + manga.author = if (author != "Updating") author else null + + var description = document.select(".description").first()?.text() ?: "" + description = description.substringAfter("The Summary is").trim() + + val otherTitle = document.select(".alternative-title").first()?.text() ?: "" + if (otherTitle != "Updating") + description += "\n\n$altName$otherTitle" + manga.description = description.trim() + val genres = mutableListOf() - document.select("a[href*=genre]").forEach { element -> - val genre = element.attr("title") + document.select(".categories a[href*=genre]").forEach { element -> + val genre = element.attr("title").removeSuffix("Genre").trim() genres.add(genre) } manga.genre = genres.joinToString(", ") - val status = when { - document.select("div.header-stats").select("strong.completed").first() != null -> SManga.COMPLETED - document.select("div.header-stats").select("strong.ongoing").first() != null -> SManga.ONGOING + + val statusElement = document.select("div.header-stats") + manga.status = when { + statusElement.select("strong.completed").isNotEmpty() -> SManga.COMPLETED + statusElement.select("strong.ongoing").isNotEmpty() -> SManga.ONGOING else -> SManga.UNKNOWN } - manga.status = status - manga.description = document.getElementsByClass("description").first().text() - val coverElement = document.getElementsByClass("cover").select("img") - val coverUrl = when { - coverElement.attr("data-src").isNotBlank() -> coverElement.attr("data-src") + + val coverElement = document.select(".cover img") + manga.thumbnail_url = when { + coverElement.attr("data-src").isNotEmpty() -> coverElement.attr("data-src") else -> coverElement.attr("src") } - manga.thumbnail_url = coverUrl - return manga } @@ -107,120 +106,93 @@ class MangaRawClub : ParsedHttpSource() { } override fun chapterFromElement(element: Element): SChapter { - val anchor = element.select("a").first() val chapter = SChapter.create() - chapter.setUrlWithoutDomain(anchor.attr("href")) - chapter.name = anchor.select(".chapter-title").first().text() - val date = anchor.select("time").first().attr("datetime") - chapter.date_upload = parseChapterDate(date) + chapter.setUrlWithoutDomain(element.select("a").attr("href")) + + val name = element.select(".chapter-title").text().removeSuffix("-eng-li") + chapter.name = "Chapter $name" + val date = parseChapterDate(element.select(".chapter-update").attr("datetime")) + if (date != null) + chapter.date_upload = date return chapter } - private fun parseChapterDate(date: String): Long { + private fun parseChapterDate(date: String): Long? { + if (date.isEmpty()) + return null // "April 21, 2021, 4:05 p.m." val fdate = date.replace(".", "").replace("Sept", "Sep") - val format = "MMMMM dd, yyyy, h:mm a" - val format2 = "MMMMM dd, yyyy, h a" // because sometimes if it is exact hour it wont have minutes because why not - val sdf = SimpleDateFormat(format, Locale.ENGLISH) return try { try { - val value = sdf.parse(fdate) - value!!.time + SimpleDateFormat("MMMMM dd, yyyy, h:mm a", Locale.ENGLISH).parse(fdate)!!.time } catch (e: ParseException) { - val sdfF = SimpleDateFormat(format2, Locale.ENGLISH) - val value = sdfF.parse(fdate) - value!!.time + // because sometimes if it is exact hour it wont have minutes + SimpleDateFormat("MMMMM dd, yyyy, h a", Locale.ENGLISH).parse(fdate)!!.time } } catch (e: ParseException) { - 0 + null } } override fun pageListParse(document: Document): List { val pages = mutableListOf() - - document.select("img[onerror][style=\"max-width: 100%;max-height: 100%;\"]").forEachIndexed { i, it -> - pages.add(Page(i, "", it.attr("src"))) + document.select(".page-in img[onerror]").forEachIndexed { i, it -> + pages.add(Page(i, imageUrl = it.attr("src"))) } - if (!pages.any()) { - document.select("img[onerror]").forEachIndexed { i, it -> - pages.add(Page(i, "", it.attr("src"))) - } - } - if (!pages.any()) { - document.select("article > div > center > img, section.page-in.content-wrap > div > center > img, section.page-in.content-wrap > center > div > center > img").forEachIndexed { i, it -> - pages.add(Page(i, "", it.attr("src"))) - } - } - return pages } override fun imageUrlParse(document: Document) = "" override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - val request = searchMangaRequest(page, query, filters) - return client.newCall(request).asObservableSuccess().map { - response -> - queryParse(response) + return client.newCall(request).asObservableSuccess().map { response -> + val mangas = mutableListOf() + val document = response.asJsoup() + document.select(searchMangaSelector()).forEach { element -> + mangas.add(searchMangaFromElement(element)) + } + val nextPage = document.select(searchMangaNextPageSelector()).isNotEmpty() + MangasPage(mangas, nextPage) } } - private fun queryParse(response: Response): MangasPage { - val mangas = mutableListOf() - val document = response.asJsoup() - document.select(latestUpdatesSelector()).forEach { element -> - mangas.add(latestUpdatesFromElement(element)) - } - val nextPage = document.select(latestUpdatesNextPageSelector()).first() != null - return MangasPage(mangas, nextPage) - } - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - if (query.isNotEmpty()) { + if (query.isNotEmpty()) // Query search return GET("$baseUrl/search/?search=$query", headers) - } + // Filter search val url = "$baseUrl/browse/".toHttpUrlOrNull()!!.newBuilder() - .addQueryParameter("results", page.toString()) val requestBody = FormBody.Builder() + url.addQueryParameter("results", page.toString()) filters.forEach { filter -> when (filter) { - is GenreList -> { - - val genreInclude = mutableListOf() - filter.state.forEach { - if (it.state == 1) { - genreInclude.add(it.name) - } - } - if (genreInclude.isNotEmpty()) { - genreInclude.forEach { genre -> - requestBody.add("options[]", genre) - } + is GenrePairList -> url.addQueryParameter("genre", filter.toUriPart()) // GET + is Order -> url.addQueryParameter("filter", filter.toUriPart()) // GET + is Status -> requestBody.add("status", filter.toUriPart()) // POST + is Action -> requestBody.add("action", filter.toUriPart()) // POST + is GenreList -> { // POST + filter.state.filter { it.state == 1 }.forEach { + requestBody.add("options[]", it.name) } } - is GenrePairList -> url.addQueryParameter("genre", filter.toUriPart()) - is Order -> url.addQueryParameter("filter", filter.toUriPart()) - is Status -> requestBody.add("status", filter.toUriPart()) - is Action -> requestBody.add("action", filter.toUriPart()) } } - return GET(url.toString(), headers) - - // val built = requestBody.build() - // return POST("$baseUrl/search", headers, requestBody.build()) - // url: String, - // headers: Headers = DEFAULT_HEADERS, - // body: RequestBody = DEFAULT_BODY, - // cache: CacheControl = DEFAULT_CACHE_CONTROL) - - // return GET(url.toString(), headers) + // return POST("$baseUrl/search", headers, requestBody.build()) // csrfmiddlewaretoken required } + override fun getFilterList() = FilterList( + Filter.Header("NOTE: Ignored if using text search!"), + Filter.Separator(), + Order(), + GenrePairList(), + // Action(), + // Status(), + // GenreList(getGenreList()) + ) + private class Action : UriPartFilter( "Action", arrayOf( @@ -236,13 +208,10 @@ class MangaRawClub : ParsedHttpSource() { Pair("Random", "Random"), Pair("Updated", "Updated"), Pair("New", "New"), - Pair("Views", "views"), + Pair("Views", "views") ) ) - private class Genre(name: String, val id: String = name) : Filter.TriState(name) - private class GenreList(genres: List) : Filter.Group("Genres", genres) - private class Status : UriPartFilter( "Status", arrayOf( @@ -272,6 +241,7 @@ class MangaRawClub : ParsedHttpSource() { Pair("Horror", "Horror"), Pair("Isekai", "Isekai"), Pair("Josei", "Josei"), + Pair("Ladies", "ladies"), Pair("Manhua", "Manhua"), Pair("Manhwa", "Manhwa"), Pair("Martial arts", "Martial arts"), @@ -291,59 +261,18 @@ class MangaRawClub : ParsedHttpSource() { Pair("Sports", "Sports"), Pair("Supernatural", "Supernatural"), Pair("Tragedy", "Tragedy"), - Pair("Webtoons", "Webtoons"), - Pair("ladies", "ladies") + Pair("Webtoons", "Webtoons") ) ) - override fun getFilterList() = FilterList( - Filter.Header("NOTE: Ignored if using text search!"), - Filter.Separator(), - // Status(), - // Action(), - Order(), - GenrePairList(), - // GenreList(getGenreList()) - ) + private fun getGenreList(): List { + return GenrePairList().vals.map { + Genre(it.first) + } + } - private fun getGenreList() = listOf( - Genre("Action"), - Genre("Adult"), - Genre("Adventure"), - Genre("Comedy"), - Genre("Cooking"), - Genre("Doujinshi"), - Genre("Drama"), - Genre("Ecchi"), - Genre("Fantasy"), - Genre("Gender bender"), - Genre("Harem"), - Genre("Historical"), - Genre("Horror"), - Genre("Isekai"), - Genre("Josei"), - Genre("Manhua"), - Genre("Manhwa"), - Genre("Martial arts"), - Genre("Mature"), - Genre("Mecha"), - Genre("Medical"), - Genre("Mystery"), - Genre("One shot"), - Genre("Psychological"), - Genre("Romance"), - Genre("School life"), - Genre("Sci fi"), - Genre("Seinen"), - Genre("Shoujo"), - Genre("Shounen"), - Genre("Slice of life"), - Genre("Sports"), - Genre("Supernatural"), - Genre("Tragedy"), - Genre("Webtoons"), - Genre("ladies") - ) + private class Genre(name: String, id: String = name) : Filter.TriState(name) + private class GenreList(genres: List) : Filter.Group("Genres+", genres) private open class UriPartFilter(displayName: String, val vals: Array>) : Filter.Select(displayName, vals.map { it.first }.toTypedArray()) {