ManhuaRock: FMReader to individual ext (#433)
* ManhuaRock: FMReader to individual ext * Add trailing newline * fix: replace hardcoded base URL
This commit is contained in:
parent
1549a6ad52
commit
198c7f053f
|
@ -15,7 +15,6 @@ class FMReaderGenerator : ThemeSourceGenerator {
|
|||
SingleLang("Epik Manga", "https://www.epikmanga.com", "tr"),
|
||||
SingleLang("KissLove", "https://klz9.com", "ja", isNsfw = true, overrideVersionCode = 4),
|
||||
SingleLang("Manga-TR", "https://manga-tr.com", "tr", className = "MangaTR", overrideVersionCode = 3),
|
||||
SingleLang("ManhuaRock", "https://manhuarock.net", "vi", overrideVersionCode = 1),
|
||||
SingleLang("Say Truyen", "https://saytruyenvip.com", "vi", overrideVersionCode = 3),
|
||||
SingleLang("WeLoveManga", "https://weloma.art", "ja", pkgName = "rawlh", isNsfw = true, overrideVersionCode = 5),
|
||||
SingleLang("Manga1000", "https://manga1000.top", "ja"),
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest />
|
|
@ -0,0 +1,7 @@
|
|||
ext {
|
||||
extName = "ManhuaRock"
|
||||
extClass = ".ManhuaRock"
|
||||
extVersionCode = 10
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,249 @@
|
|||
package eu.kanade.tachiyomi.extension.vi.manhuarock
|
||||
|
||||
import android.util.Log
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
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.source.online.ParsedHttpSource
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
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 rx.Single
|
||||
import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
class ManhuaRock : ParsedHttpSource() {
|
||||
|
||||
// Site changed from FMReader to some Madara copycat
|
||||
override val versionId = 2
|
||||
|
||||
override val name = "ManhuaRock"
|
||||
|
||||
override val lang = "vi"
|
||||
|
||||
override val baseUrl = "https://manhuarockz.com"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override fun headersBuilder() = super.headersBuilder()
|
||||
.add("Referer", "$baseUrl/")
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val dateFormat by lazy {
|
||||
SimpleDateFormat("dd MMM yyyy", Locale.US).apply {
|
||||
timeZone = TimeZone.getTimeZone("Asia/Ho_Chi_Minh")
|
||||
}
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/tat-ca-truyen/$page/?sort=most-viewd")
|
||||
|
||||
override fun popularMangaSelector() = "div.listupd div.page-item"
|
||||
|
||||
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
|
||||
val a = element.selectFirst("a")!!
|
||||
|
||||
setUrlWithoutDomain(a.attr("abs:href"))
|
||||
title = a.attr("title")
|
||||
thumbnail_url = element.selectFirst("img")?.attr("abs:data-src")
|
||||
}
|
||||
|
||||
override fun popularMangaNextPageSelector() = "li.next:not(.disabled)"
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/tat-ca-truyen/$page/?sort=latest-updated")
|
||||
|
||||
override fun latestUpdatesSelector() = popularMangaSelector()
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = baseUrl.toHttpUrl().newBuilder().apply {
|
||||
if (query.isNotBlank()) {
|
||||
addPathSegment("search")
|
||||
addPathSegment(page.toString())
|
||||
addQueryParameter("keyword", query)
|
||||
} else {
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach {
|
||||
when (it) {
|
||||
is OrderByFilter -> addQueryParameter("sort", it.values[it.state].slug)
|
||||
is GenreList -> addPathSegments(it.values[it.state].slug)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
addPathSegment(page.toString())
|
||||
}
|
||||
}.build()
|
||||
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
|
||||
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
|
||||
|
||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
||||
title = document.selectFirst("div.post-title h1")!!.text()
|
||||
author = document.selectFirst("div.author-content")?.text()
|
||||
artist = document.selectFirst("div.artist-content")?.text()
|
||||
description = document.selectFirst("div.dsct")?.text()
|
||||
genre = document.select("div.genres-content a[rel=tag]").joinToString { it.text() }
|
||||
status = when (document.selectFirst("div.summary-heading:contains(Tình Trạng) + div.summary-content")?.text()) {
|
||||
// I have zero idea what the strings for Ongoing and Completed are, these are educated guesses
|
||||
// All the metadata on this page is basically "Unknown".
|
||||
"Đang Ra" -> SManga.ONGOING
|
||||
"Hoàn Thành" -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
thumbnail_url = document.selectFirst("div.summary_image img")?.attr("abs:data-src")
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = "ul.row-content-chapter li"
|
||||
|
||||
override fun chapterFromElement(element: Element) = SChapter.create().apply {
|
||||
val a = element.selectFirst("a")!!
|
||||
|
||||
setUrlWithoutDomain(a.attr("abs:href"))
|
||||
name = a.text()
|
||||
date_upload = runCatching {
|
||||
val date = element.selectFirst("span.chapter-time")!!.text()
|
||||
|
||||
dateFormat.parse(date)!!.time
|
||||
}.getOrDefault(0L)
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
val chapterId = chapter.url.split('/').last()
|
||||
|
||||
return GET("$baseUrl/ajax/image/list/chap/$chapterId?mode=vertical&quality=high")
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val chapterId = response.request.url.pathSegments.last()
|
||||
|
||||
countViews(chapterId)
|
||||
|
||||
val data = json.decodeFromString<AjaxImageListResponse>(response.body.string())
|
||||
|
||||
if (!data.status || data.html == null) {
|
||||
throw Exception(data.msg ?: "Lỗi không xác định khi lấy trang")
|
||||
}
|
||||
|
||||
return pageListParse(Jsoup.parse(data.html, baseUrl))
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
return document.select("img").mapIndexed { i, it ->
|
||||
Page(i, imageUrl = it.attr("abs:data-src"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
Filter.Header("Không dùng chung với tìm kiếm bằng từ khoá."),
|
||||
OrderByFilter(),
|
||||
GenreList(getGenreList()),
|
||||
)
|
||||
|
||||
private fun countViews(chapterId: String) {
|
||||
val req = POST("$baseUrl/ajax/manga/count-view/$chapterId")
|
||||
|
||||
Single.fromCallable {
|
||||
client.newCall(req).execute().close()
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{},
|
||||
{
|
||||
Log.e("manhuarock", "Could not count chapter view: ${it.stackTraceToString()}")
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class AjaxImageListResponse(
|
||||
val status: Boolean = false,
|
||||
val msg: String? = null,
|
||||
val html: String? = null,
|
||||
)
|
||||
|
||||
private class Slug(val name: String, val slug: String) {
|
||||
override fun toString() = name
|
||||
}
|
||||
|
||||
private class OrderByFilter : Filter.Select<Slug>(
|
||||
"Sắp xếp theo",
|
||||
arrayOf(
|
||||
Slug("Mới cập nhật", "latest-updated"),
|
||||
Slug("Điểm", "score"),
|
||||
Slug("Tên A-Z", "name-az"),
|
||||
Slug("Ngày phát hành", "release-date"),
|
||||
Slug("Xem nhiều", "most-viewd"),
|
||||
),
|
||||
4,
|
||||
)
|
||||
|
||||
private class GenreList(slugs: Array<Slug>) : Filter.Select<Slug>("Thể loại", slugs)
|
||||
|
||||
private fun getGenreList() = arrayOf(
|
||||
Slug("Tất cả", "tat-ca-truyen"),
|
||||
Slug("Hoàn thành", "hoan-thanh"),
|
||||
Slug("Xuyên Không", "the-loai/xuyen-khong"),
|
||||
Slug("Webtoon", "the-loai/webtoon"),
|
||||
Slug("Truyện Màu", "the-loai/truyen-mau"),
|
||||
Slug("Trọng Sinh", "the-loai/trong-sinh"),
|
||||
Slug("Tragedy", "the-loai/tragedy"),
|
||||
Slug("Supernatural", "the-loai/supernatural"),
|
||||
Slug("Sports", "the-loai/sports"),
|
||||
Slug("Slice Of Life", "the-loai/slice-of-life"),
|
||||
Slug("Shounen", "the-loai/shounen"),
|
||||
Slug("Shoujo", "the-loai/shoujo"),
|
||||
Slug("Sci-Fi", "the-loai/sci-fi"),
|
||||
Slug("School Life", "the-loai/school-life"),
|
||||
Slug("Romance", "the-loai/romance"),
|
||||
Slug("Psychological", "the-loai/psychological"),
|
||||
Slug("Ngôn Tình", "the-loai/ngon-tinh"),
|
||||
Slug("Mystery", "the-loai/mystery"),
|
||||
Slug("Mature", "the-loai/mature"),
|
||||
Slug("Martial Arts", "the-loai/martial-arts"),
|
||||
Slug("Manhwa", "the-loai/manhwa"),
|
||||
Slug("Manhua", "the-loai/manhua"),
|
||||
Slug("Josei", "the-loai/josei"),
|
||||
Slug("Isekai", "the-loai/isekai"),
|
||||
Slug("Huyền Huyễn", "the-loai/huyen-huyen"),
|
||||
Slug("Horror", "the-loai/horror"),
|
||||
Slug("Historical", "the-loai/historical"),
|
||||
Slug("Harem", "the-loai/harem"),
|
||||
Slug("Gender Bender", "the-loai/gender-bender"),
|
||||
Slug("Fantasy", "the-loai/fantasy"),
|
||||
Slug("Ecchi", "the-loai/ecchi"),
|
||||
Slug("Drama", "the-loai/drama"),
|
||||
Slug("Detective", "the-loai/detective"),
|
||||
Slug("Demons", "the-loai/demons"),
|
||||
Slug("Comedy", "the-loai/comedy"),
|
||||
Slug("Cổ Đại", "the-loai/co-dai"),
|
||||
Slug("Chuyển Sinh", "the-loai/chuyen-sinh"),
|
||||
Slug("Anime", "the-loai/anime"),
|
||||
Slug("Adventure", "the-loai/adventure"),
|
||||
Slug("Adult", "the-loai/adult"),
|
||||
Slug("Action", "the-loai/action"),
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue