Manga-Raw.club: Refactor (#11036)

Improved chapter title
Added alternative title in manga description
This commit is contained in:
Davide 2022-03-07 14:11:04 +01:00 committed by GitHub
parent 2b5359ca26
commit e50ade5026
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 93 additions and 164 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'manga-raw.club' extName = 'manga-raw.club'
pkgNameSuffix = 'en.mangarawclub' pkgNameSuffix = 'en.mangarawclub'
extClass = '.MangaRawClub' extClass = '.MangaRawClub'
extVersionCode = 6 extVersionCode = 7
isNsfw = true isNsfw = true
} }

View File

@ -14,7 +14,6 @@ import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
@ -34,6 +33,10 @@ class MangaRawClub : ParsedHttpSource() {
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.build() .build()
companion object {
const val altName = "Alternative Name: "
}
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/browse/?results=$page&filter=views", headers) return GET("$baseUrl/browse/?results=$page&filter=views", headers)
} }
@ -42,60 +45,56 @@ class MangaRawClub : ParsedHttpSource() {
return GET("$baseUrl/listy/manga/?results=$page", headers) 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 searchMangaFromElement(element: Element): SManga {
override fun latestUpdatesSelector() = popularMangaSelector()
override fun searchMangaSelector() = "ul.novel-list.grid.col.col1 > li"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
val coverElement = element.getElementsByClass("novel-cover").first() manga.title = element.select(".novel-title").first()?.text() ?: ""
var titleElement = element.getElementsByClass("novel-title text1row").first() manga.thumbnail_url = element.select(".novel-cover img").attr("abs:data-src")
if (titleElement == null) {
titleElement = element.getElementsByClass("novel-title text2row").first()
}
manga.thumbnail_url = coverElement.select("img").attr("abs:data-src")
manga.setUrlWithoutDomain(element.select("a").first().attr("href")) manga.setUrlWithoutDomain(element.select("a").first().attr("href"))
manga.title = titleElement.text()
return manga return manga
} }
override fun popularMangaFromElement(element: Element): SManga = searchMangaFromElement(element)
override fun latestUpdatesFromElement(element: Element): SManga = searchMangaFromElement(element)
override fun latestUpdatesFromElement(element: Element): SManga = override fun searchMangaNextPageSelector() = "ul.pagination > li:last-child > a"
popularMangaFromElement(element) override fun popularMangaNextPageSelector() = searchMangaNextPageSelector()
override fun latestUpdatesNextPageSelector() = searchMangaNextPageSelector()
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 mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create() val manga = SManga.create()
val authorElement = document.getElementsByClass("author").first() val author = document.select(".author a").first()?.attr("title") ?: ""
manga.author = authorElement.select("a").attr("title") manga.author = if (author != "Updating") author else null
// manga.author = document.select("a[class=\"property-item\"]").first().attr("title")
manga.artist = "" 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<String>() val genres = mutableListOf<String>()
document.select("a[href*=genre]").forEach { element -> document.select(".categories a[href*=genre]").forEach { element ->
val genre = element.attr("title") val genre = element.attr("title").removeSuffix("Genre").trim()
genres.add(genre) genres.add(genre)
} }
manga.genre = genres.joinToString(", ") manga.genre = genres.joinToString(", ")
val status = when {
document.select("div.header-stats").select("strong.completed").first() != null -> SManga.COMPLETED val statusElement = document.select("div.header-stats")
document.select("div.header-stats").select("strong.ongoing").first() != null -> SManga.ONGOING manga.status = when {
statusElement.select("strong.completed").isNotEmpty() -> SManga.COMPLETED
statusElement.select("strong.ongoing").isNotEmpty() -> SManga.ONGOING
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
manga.status = status
manga.description = document.getElementsByClass("description").first().text() val coverElement = document.select(".cover img")
val coverElement = document.getElementsByClass("cover").select("img") manga.thumbnail_url = when {
val coverUrl = when { coverElement.attr("data-src").isNotEmpty() -> coverElement.attr("data-src")
coverElement.attr("data-src").isNotBlank() -> coverElement.attr("data-src")
else -> coverElement.attr("src") else -> coverElement.attr("src")
} }
manga.thumbnail_url = coverUrl
return manga return manga
} }
@ -107,120 +106,93 @@ class MangaRawClub : ParsedHttpSource() {
} }
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {
val anchor = element.select("a").first()
val chapter = SChapter.create() val chapter = SChapter.create()
chapter.setUrlWithoutDomain(anchor.attr("href")) chapter.setUrlWithoutDomain(element.select("a").attr("href"))
chapter.name = anchor.select(".chapter-title").first().text()
val date = anchor.select("time").first().attr("datetime") val name = element.select(".chapter-title").text().removeSuffix("-eng-li")
chapter.date_upload = parseChapterDate(date) chapter.name = "Chapter $name"
val date = parseChapterDate(element.select(".chapter-update").attr("datetime"))
if (date != null)
chapter.date_upload = date
return chapter 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." // "April 21, 2021, 4:05 p.m."
val fdate = date.replace(".", "").replace("Sept", "Sep") 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 { return try {
try { try {
val value = sdf.parse(fdate) SimpleDateFormat("MMMMM dd, yyyy, h:mm a", Locale.ENGLISH).parse(fdate)!!.time
value!!.time
} catch (e: ParseException) { } catch (e: ParseException) {
val sdfF = SimpleDateFormat(format2, Locale.ENGLISH) // because sometimes if it is exact hour it wont have minutes
val value = sdfF.parse(fdate) SimpleDateFormat("MMMMM dd, yyyy, h a", Locale.ENGLISH).parse(fdate)!!.time
value!!.time
} }
} catch (e: ParseException) { } catch (e: ParseException) {
0 null
} }
} }
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>() val pages = mutableListOf<Page>()
document.select(".page-in img[onerror]").forEachIndexed { i, it ->
document.select("img[onerror][style=\"max-width: 100%;max-height: 100%;\"]").forEachIndexed { i, it -> pages.add(Page(i, imageUrl = it.attr("src")))
pages.add(Page(i, "", 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 return pages
} }
override fun imageUrlParse(document: Document) = "" override fun imageUrlParse(document: Document) = ""
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
val request = searchMangaRequest(page, query, filters) val request = searchMangaRequest(page, query, filters)
return client.newCall(request).asObservableSuccess().map { return client.newCall(request).asObservableSuccess().map { response ->
response -> val mangas = mutableListOf<SManga>()
queryParse(response) 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<SManga>()
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 { 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) return GET("$baseUrl/search/?search=$query", headers)
}
// Filter search
val url = "$baseUrl/browse/".toHttpUrlOrNull()!!.newBuilder() val url = "$baseUrl/browse/".toHttpUrlOrNull()!!.newBuilder()
.addQueryParameter("results", page.toString())
val requestBody = FormBody.Builder() val requestBody = FormBody.Builder()
url.addQueryParameter("results", page.toString())
filters.forEach { filter -> filters.forEach { filter ->
when (filter) { when (filter) {
is GenreList -> { is GenrePairList -> url.addQueryParameter("genre", filter.toUriPart()) // GET
is Order -> url.addQueryParameter("filter", filter.toUriPart()) // GET
val genreInclude = mutableListOf<String>() is Status -> requestBody.add("status", filter.toUriPart()) // POST
filter.state.forEach { is Action -> requestBody.add("action", filter.toUriPart()) // POST
if (it.state == 1) { is GenreList -> { // POST
genreInclude.add(it.name) filter.state.filter { it.state == 1 }.forEach {
} requestBody.add("options[]", it.name)
}
if (genreInclude.isNotEmpty()) {
genreInclude.forEach { genre ->
requestBody.add("options[]", genre)
}
} }
} }
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) return GET(url.toString(), headers)
// return POST("$baseUrl/search", headers, requestBody.build()) // csrfmiddlewaretoken required
// 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)
} }
override fun getFilterList() = FilterList(
Filter.Header("NOTE: Ignored if using text search!"),
Filter.Separator(),
Order(),
GenrePairList(),
// Action(),
// Status(),
// GenreList(getGenreList())
)
private class Action : UriPartFilter( private class Action : UriPartFilter(
"Action", "Action",
arrayOf( arrayOf(
@ -236,13 +208,10 @@ class MangaRawClub : ParsedHttpSource() {
Pair("Random", "Random"), Pair("Random", "Random"),
Pair("Updated", "Updated"), Pair("Updated", "Updated"),
Pair("New", "New"), 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<Genre>) : Filter.Group<Genre>("Genres", genres)
private class Status : UriPartFilter( private class Status : UriPartFilter(
"Status", "Status",
arrayOf( arrayOf(
@ -272,6 +241,7 @@ class MangaRawClub : ParsedHttpSource() {
Pair("Horror", "Horror"), Pair("Horror", "Horror"),
Pair("Isekai", "Isekai"), Pair("Isekai", "Isekai"),
Pair("Josei", "Josei"), Pair("Josei", "Josei"),
Pair("Ladies", "ladies"),
Pair("Manhua", "Manhua"), Pair("Manhua", "Manhua"),
Pair("Manhwa", "Manhwa"), Pair("Manhwa", "Manhwa"),
Pair("Martial arts", "Martial arts"), Pair("Martial arts", "Martial arts"),
@ -291,59 +261,18 @@ class MangaRawClub : ParsedHttpSource() {
Pair("Sports", "Sports"), Pair("Sports", "Sports"),
Pair("Supernatural", "Supernatural"), Pair("Supernatural", "Supernatural"),
Pair("Tragedy", "Tragedy"), Pair("Tragedy", "Tragedy"),
Pair("Webtoons", "Webtoons"), Pair("Webtoons", "Webtoons")
Pair("ladies", "ladies")
) )
) )
override fun getFilterList() = FilterList( private fun getGenreList(): List<Genre> {
Filter.Header("NOTE: Ignored if using text search!"), return GenrePairList().vals.map {
Filter.Separator(), Genre(it.first)
// Status(), }
// Action(), }
Order(),
GenrePairList(),
// GenreList(getGenreList())
)
private fun getGenreList() = listOf( private class Genre(name: String, id: String = name) : Filter.TriState(name)
Genre("Action"), private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres+", genres)
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 open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) : private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) { Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {