Manga-Raw.club: Refactor (#11036)
Improved chapter title Added alternative title in manga description
This commit is contained in:
parent
2b5359ca26
commit
e50ade5026
|
@ -5,7 +5,7 @@ ext {
|
|||
extName = 'manga-raw.club'
|
||||
pkgNameSuffix = 'en.mangarawclub'
|
||||
extClass = '.MangaRawClub'
|
||||
extVersionCode = 6
|
||||
extVersionCode = 7
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String>()
|
||||
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<Page> {
|
||||
val pages = mutableListOf<Page>()
|
||||
|
||||
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<MangasPage> {
|
||||
|
||||
val request = searchMangaRequest(page, query, filters)
|
||||
return client.newCall(request).asObservableSuccess().map {
|
||||
response ->
|
||||
queryParse(response)
|
||||
}
|
||||
}
|
||||
|
||||
private fun queryParse(response: Response): MangasPage {
|
||||
return client.newCall(request).asObservableSuccess().map { response ->
|
||||
val mangas = mutableListOf<SManga>()
|
||||
val document = response.asJsoup()
|
||||
document.select(latestUpdatesSelector()).forEach { element ->
|
||||
mangas.add(latestUpdatesFromElement(element))
|
||||
document.select(searchMangaSelector()).forEach { element ->
|
||||
mangas.add(searchMangaFromElement(element))
|
||||
}
|
||||
val nextPage = document.select(searchMangaNextPageSelector()).isNotEmpty()
|
||||
MangasPage(mangas, nextPage)
|
||||
}
|
||||
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<String>()
|
||||
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<Genre>) : Filter.Group<Genre>("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<Genre> {
|
||||
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<Genre>) : Filter.Group<Genre>("Genres+", genres)
|
||||
|
||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
|
|
Loading…
Reference in New Issue