Update Manhwa18/Manhwa18.net, Manhwaraw.com (#19516)
* Fix ManyToon.me & ManyToon.club Fix issues in which both sources unable to load any mangas. Closes #16612 * Update multisrc MyMangaCMS to support other site’s langugae Also, support a second alternative name and remove generic words like “manhwa”, “engsub” from those names. Also, mark TruyenTranhLH as NSFW * Move Manhwa18 & Manhwa18.net to MyMangaCMS theme. Also fix Manhwa18.net’s searching issue with the previous theme. Also allow Manhwa18’s filter working since it was not with the previous theme. Close #18818 * Remove unused import causing PR build check to fail * MyMangaCms Bump the `versionId` so the ID will change and users will be forced to migrate to update the URLs. * fix locale friendly * Fix Manhwaraw.com won’t show Popular & Latest manhwa * revert bumping TruyenTranhLH’s version
@ -1,90 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.en.manhwa18
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.fmreader.FMReader
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import org.jsoup.nodes.Document
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class Manhwa18 : FMReader("Manhwa18", "https://manhwa18.com", "en") {
|
|
||||||
override val requestPath = "tim-kiem"
|
|
||||||
|
|
||||||
override val popularSort = "sort=top"
|
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response): MangasPage {
|
|
||||||
val document = response.asJsoup()
|
|
||||||
|
|
||||||
val mangas = document.select(popularMangaSelector()).map { popularMangaFromElement(it) }
|
|
||||||
|
|
||||||
return MangasPage(mangas, document.select(".pagination_wrap .disabled").text() != "Bottom")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
|
||||||
val url = "$baseUrl/$requestPath?".toHttpUrlOrNull()!!.newBuilder()
|
|
||||||
.addQueryParameter("q", query)
|
|
||||||
.addQueryParameter("page", page.toString())
|
|
||||||
return GET(url.toString(), headers)
|
|
||||||
}
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request =
|
|
||||||
GET("$baseUrl/$requestPath?listType=pagination&page=$page&sort=update&sort_type=DESC", headers)
|
|
||||||
|
|
||||||
override fun mangaDetailsParse(document: Document): SManga {
|
|
||||||
return SManga.create().apply {
|
|
||||||
title = document.select(".series-name").text()
|
|
||||||
thumbnail_url = document.select("meta[property='og:image']").attr("abs:content")
|
|
||||||
|
|
||||||
document.select(".series-information")?.let { info ->
|
|
||||||
author = info.select(".info-name:contains(Author:) + .info-value").text()
|
|
||||||
genre = info.select(".info-name:contains(Genre:) + .info-value > a")
|
|
||||||
.joinToString { it.text().trim() }
|
|
||||||
|
|
||||||
description = document.select(".summary-content").text().trim()
|
|
||||||
info.select(".info-name:contains(Other name:) + .info-value")
|
|
||||||
.firstOrNull()?.text()?.let {
|
|
||||||
val altName = removeGenericWords(it)
|
|
||||||
description = when (title.lowercase(Locale.US)) {
|
|
||||||
altName.lowercase(Locale.US) -> description
|
|
||||||
else -> description + "\n\n$altName"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
status =
|
|
||||||
parseStatus(info.select(".info-name:contains(Status:) + .info-value").text())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun removeGenericWords(name: String): String {
|
|
||||||
val excludeList = listOf("manhwa", "engsub")
|
|
||||||
return name.split(' ').filterNot { word ->
|
|
||||||
word.lowercase(Locale.US) in excludeList
|
|
||||||
}.joinToString(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
|
||||||
val document = response.asJsoup()
|
|
||||||
|
|
||||||
return document.select(".list-chapters > a").map { element ->
|
|
||||||
SChapter.create().apply {
|
|
||||||
setUrlWithoutDomain(element.attr("abs:href"))
|
|
||||||
name = element.attr("title")
|
|
||||||
date_upload =
|
|
||||||
SimpleDateFormat("dd/MM/yyyy", Locale.US).parse(
|
|
||||||
element.select(".chapter-time").text().substringAfter(" - "),
|
|
||||||
)?.time ?: 0L
|
|
||||||
chapter_number = element.attr("time").substringAfterLast(' ').toFloatOrNull() ?: -1f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val pageListImageSelector = "#chapter-content > img"
|
|
||||||
|
|
||||||
override fun getFilterList() = FilterList()
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.all.manhwa18net
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.fmreader.FMReader
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
|
||||||
import okhttp3.Request
|
|
||||||
import org.jsoup.nodes.Element
|
|
||||||
|
|
||||||
class Manhwa18NetFactory : SourceFactory {
|
|
||||||
override fun createSources(): List<Source> = listOf(
|
|
||||||
Manhwa18Net(),
|
|
||||||
Manhwa18NetRaw(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Manhwa18Net : FMReader("Manhwa18.net", "https://manhwa18.net", "en") {
|
|
||||||
override val requestPath = "genre/manhwa"
|
|
||||||
override val popularSort = "sort=top"
|
|
||||||
override val pageListImageSelector = "div#chapter-content > img"
|
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request =
|
|
||||||
GET(
|
|
||||||
"$baseUrl/$requestPath?listType=pagination&page=$page&sort=update&sort_type=DESC",
|
|
||||||
headers,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
|
||||||
val noRawsUrl = super.searchMangaRequest(page, query, filters).url.newBuilder().toString()
|
|
||||||
return GET(noRawsUrl, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getGenreList() = getAdultGenreList()
|
|
||||||
|
|
||||||
override fun chapterFromElement(element: Element, mangaTitle: String): SChapter {
|
|
||||||
return SChapter.create().apply {
|
|
||||||
setUrlWithoutDomain(element.attr("abs:href"))
|
|
||||||
name = element.attr("title")
|
|
||||||
date_upload = parseAbsoluteDate(
|
|
||||||
element.select(chapterTimeSelector).text().substringAfter(" - "),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Manhwa18NetRaw : FMReader("Manhwa18.net", "https://manhwa18.net", "ko") {
|
|
||||||
override val requestPath = "genre/raw"
|
|
||||||
override val popularSort = "sort=top"
|
|
||||||
override val pageListImageSelector = "div#chapter-content > img"
|
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request =
|
|
||||||
GET(
|
|
||||||
"$baseUrl/$requestPath?listType=pagination&page=$page&sort=update&sort_type=DESC",
|
|
||||||
headers,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
|
||||||
val onlyRawsUrl = super.searchMangaRequest(page, query, filters).url.newBuilder().toString()
|
|
||||||
return GET(onlyRawsUrl, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFilterList() = FilterList(
|
|
||||||
super.getFilterList().filterNot { it == GenreList(getGenreList()) },
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun chapterFromElement(element: Element, mangaTitle: String): SChapter {
|
|
||||||
return SChapter.create().apply {
|
|
||||||
setUrlWithoutDomain(element.attr("abs:href"))
|
|
||||||
name = element.attr("title")
|
|
||||||
date_upload = parseAbsoluteDate(
|
|
||||||
element.select(chapterTimeSelector).text().substringAfter(" - "),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
11
multisrc/overrides/madara/manhwaraw/src/ManhwaRaw.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.ko.manhwaraw
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||||
|
|
||||||
|
class ManhwaRaw : Madara("ManhwaRaw", "https://manhwaraw.com", "ko") {
|
||||||
|
|
||||||
|
override val mangaSubString = "manhwa-raw"
|
||||||
|
|
||||||
|
// The website does not flag the content.
|
||||||
|
override val filterNonMangaItems = false
|
||||||
|
}
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
62
multisrc/overrides/mymangacms/manhwa18/src/Manhwa18.kt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.en.manhwa18
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mymangacms.MyMangaCMS
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
|
||||||
|
class Manhwa18 : MyMangaCMS("Manhwa18", "https://manhwa18.com", "en") {
|
||||||
|
|
||||||
|
// Migrated from FMReader to MyMangaCMS.
|
||||||
|
override val versionId = 2
|
||||||
|
|
||||||
|
override val parseAuthorString = "Author"
|
||||||
|
override val parseAlternativeNameString = "Other name"
|
||||||
|
override val parseAlternative2ndNameString = "Doujinshi"
|
||||||
|
override val parseStatusString = "Status"
|
||||||
|
override val parseStatusOngoingStringLowerCase = "on going"
|
||||||
|
override val parseStatusOnHoldStringLowerCase = "on hold"
|
||||||
|
override val parseStatusCompletedStringLowerCase = "completed"
|
||||||
|
|
||||||
|
override fun getFilterList(): FilterList = FilterList(
|
||||||
|
Author("Author"),
|
||||||
|
Status(
|
||||||
|
"Status",
|
||||||
|
"All",
|
||||||
|
"Ongoing",
|
||||||
|
"On hold",
|
||||||
|
"Completed",
|
||||||
|
),
|
||||||
|
Sort(
|
||||||
|
"Order",
|
||||||
|
"A-Z",
|
||||||
|
"Z-A",
|
||||||
|
"Latest update",
|
||||||
|
"New manhwa",
|
||||||
|
"Most view",
|
||||||
|
"Most like",
|
||||||
|
),
|
||||||
|
GenreList(getGenreList(), "Genre"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// To populate this list:
|
||||||
|
// console.log([...document.querySelectorAll("div.search-gerne_item")].map(elem => `Genre("${elem.textContent.trim()}", ${elem.querySelector("label").getAttribute("data-genre-id")}),`).join("\n"))
|
||||||
|
override fun getGenreList() = listOf(
|
||||||
|
Genre("Adult", 4),
|
||||||
|
Genre("Doujinshi", 9),
|
||||||
|
Genre("Harem", 17),
|
||||||
|
Genre("Manga", 24),
|
||||||
|
Genre("Manhwa", 26),
|
||||||
|
Genre("Mature", 28),
|
||||||
|
Genre("NTR", 33),
|
||||||
|
Genre("Romance", 36),
|
||||||
|
Genre("Webtoon", 57),
|
||||||
|
Genre("Action", 59),
|
||||||
|
Genre("Comedy", 60),
|
||||||
|
Genre("BL", 61),
|
||||||
|
Genre("Horror", 62),
|
||||||
|
Genre("Raw", 63),
|
||||||
|
Genre("Uncensore", 64),
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun dateUpdatedParser(date: String): Long =
|
||||||
|
runCatching { dateFormatter.parse(date.substringAfter(" - "))?.time }.getOrNull() ?: 0L
|
||||||
|
}
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
62
multisrc/overrides/mymangacms/manhwa18net/src/Manhwa18Net.kt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.all.manhwa18net
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mymangacms.MyMangaCMS
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
|
||||||
|
class Manhwa18Net : MyMangaCMS("Manhwa18.net", "https://manhwa18.net", "en") {
|
||||||
|
|
||||||
|
// Migrated from FMReader to MyMangaCms.
|
||||||
|
override val versionId = 2
|
||||||
|
|
||||||
|
override val parseAuthorString = "Author"
|
||||||
|
override val parseAlternativeNameString = "Other name"
|
||||||
|
override val parseAlternative2ndNameString = "Doujinshi"
|
||||||
|
override val parseStatusString = "Status"
|
||||||
|
override val parseStatusOngoingStringLowerCase = "on going"
|
||||||
|
override val parseStatusOnHoldStringLowerCase = "on hold"
|
||||||
|
override val parseStatusCompletedStringLowerCase = "completed"
|
||||||
|
|
||||||
|
override fun getFilterList(): FilterList = FilterList(
|
||||||
|
Author("Author"),
|
||||||
|
Status(
|
||||||
|
"Status",
|
||||||
|
"All",
|
||||||
|
"Ongoing",
|
||||||
|
"On hold",
|
||||||
|
"Completed",
|
||||||
|
),
|
||||||
|
Sort(
|
||||||
|
"Order",
|
||||||
|
"A-Z",
|
||||||
|
"Z-A",
|
||||||
|
"Latest update",
|
||||||
|
"New manhwa",
|
||||||
|
"Most view",
|
||||||
|
"Most like",
|
||||||
|
),
|
||||||
|
GenreList(getGenreList(), "Genre"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// To populate this list:
|
||||||
|
// console.log([...document.querySelectorAll("div.search-gerne_item")].map(elem => `Genre("${elem.textContent.trim()}", ${elem.querySelector("label").getAttribute("data-genre-id")}),`).join("\n"))
|
||||||
|
override fun getGenreList() = listOf(
|
||||||
|
Genre("Adult", 4),
|
||||||
|
Genre("Doujinshi", 9),
|
||||||
|
Genre("Harem", 17),
|
||||||
|
Genre("Manga", 24),
|
||||||
|
Genre("Manhwa", 26),
|
||||||
|
Genre("Mature", 28),
|
||||||
|
Genre("NTR", 33),
|
||||||
|
Genre("Romance", 36),
|
||||||
|
Genre("Webtoon", 57),
|
||||||
|
Genre("Action", 59),
|
||||||
|
Genre("Comedy", 60),
|
||||||
|
Genre("BL", 61),
|
||||||
|
Genre("Horror", 62),
|
||||||
|
Genre("Raw", 63),
|
||||||
|
Genre("Uncensore", 64),
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun dateUpdatedParser(date: String): Long =
|
||||||
|
runCatching { dateFormatter.parse(date.substringAfter(" - "))?.time }.getOrNull() ?: 0L
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.multisrc.fmreader
|
package eu.kanade.tachiyomi.multisrc.fmreader
|
||||||
|
|
||||||
import generator.ThemeSourceData.MultiLang
|
|
||||||
import generator.ThemeSourceData.SingleLang
|
import generator.ThemeSourceData.SingleLang
|
||||||
import generator.ThemeSourceGenerator
|
import generator.ThemeSourceGenerator
|
||||||
|
|
||||||
@ -13,12 +12,10 @@ class FMReaderGenerator : ThemeSourceGenerator {
|
|||||||
override val baseVersionCode: Int = 8
|
override val baseVersionCode: Int = 8
|
||||||
|
|
||||||
override val sources = listOf(
|
override val sources = listOf(
|
||||||
MultiLang("Manhwa18.net", "https://manhwa18.net", listOf("en", "ko"), className = "Manhwa18NetFactory", isNsfw = true, overrideVersionCode = 1),
|
|
||||||
SingleLang("Epik Manga", "https://www.epikmanga.com", "tr"),
|
SingleLang("Epik Manga", "https://www.epikmanga.com", "tr"),
|
||||||
SingleLang("KissLove", "https://klz9.com", "ja", isNsfw = true, overrideVersionCode = 4),
|
SingleLang("KissLove", "https://klz9.com", "ja", isNsfw = true, overrideVersionCode = 4),
|
||||||
SingleLang("Manga-TR", "https://manga-tr.com", "tr", className = "MangaTR", overrideVersionCode = 2),
|
SingleLang("Manga-TR", "https://manga-tr.com", "tr", className = "MangaTR", overrideVersionCode = 2),
|
||||||
SingleLang("ManhuaRock", "https://manhuarock.net", "vi", overrideVersionCode = 1),
|
SingleLang("ManhuaRock", "https://manhuarock.net", "vi", overrideVersionCode = 1),
|
||||||
SingleLang("Manhwa18", "https://manhwa18.com", "en", isNsfw = true, overrideVersionCode = 2),
|
|
||||||
SingleLang("Say Truyen", "https://saytruyenvip.com", "vi", overrideVersionCode = 3),
|
SingleLang("Say Truyen", "https://saytruyenvip.com", "vi", overrideVersionCode = 3),
|
||||||
SingleLang("WeLoveManga", "https://weloma.art", "ja", pkgName = "rawlh", isNsfw = true, overrideVersionCode = 5),
|
SingleLang("WeLoveManga", "https://weloma.art", "ja", pkgName = "rawlh", isNsfw = true, overrideVersionCode = 5),
|
||||||
SingleLang("Manga1000", "https://manga1000.top", "ja"),
|
SingleLang("Manga1000", "https://manga1000.top", "ja"),
|
||||||
|
@ -329,7 +329,7 @@ class MadaraGenerator : ThemeSourceGenerator {
|
|||||||
SingleLang("ManhuaScan.info (unoriginal)", "https://manhuascan.info", "en", isNsfw = true, className = "ManhuaScanInfo"),
|
SingleLang("ManhuaScan.info (unoriginal)", "https://manhuascan.info", "en", isNsfw = true, className = "ManhuaScanInfo"),
|
||||||
SingleLang("ManhuaUS", "https://manhuaus.com", "en", overrideVersionCode = 5),
|
SingleLang("ManhuaUS", "https://manhuaus.com", "en", overrideVersionCode = 5),
|
||||||
SingleLang("ManhuaZone", "https://manhuazone.org", "en", overrideVersionCode = 1),
|
SingleLang("ManhuaZone", "https://manhuazone.org", "en", overrideVersionCode = 1),
|
||||||
SingleLang("Manhwa Raw", "https://manhwaraw.com", "ko", isNsfw = true, overrideVersionCode = 1),
|
SingleLang("Manhwa Raw", "https://manhwaraw.com", "ko", isNsfw = true, overrideVersionCode = 2),
|
||||||
SingleLang("Manhwa-Latino", "https://manhwa-latino.com", "es", isNsfw = true, className = "ManhwaLatino", overrideVersionCode = 7),
|
SingleLang("Manhwa-Latino", "https://manhwa-latino.com", "es", isNsfw = true, className = "ManhwaLatino", overrideVersionCode = 7),
|
||||||
SingleLang("Manhwa-raw", "https://manhwa-raw.com", "all", isNsfw = true, className = "ManhwaDashRaw", overrideVersionCode = 1),
|
SingleLang("Manhwa-raw", "https://manhwa-raw.com", "all", isNsfw = true, className = "ManhwaDashRaw", overrideVersionCode = 1),
|
||||||
SingleLang("Manhwa18.app", "https://manhwa18.app", "en", isNsfw = true, className = "Manhwa18app"),
|
SingleLang("Manhwa18.app", "https://manhwa18.app", "en", isNsfw = true, className = "Manhwa18app"),
|
||||||
|
@ -29,6 +29,19 @@ abstract class MyMangaCMS(
|
|||||||
override val lang: String,
|
override val lang: String,
|
||||||
) : ParsedHttpSource() {
|
) : ParsedHttpSource() {
|
||||||
|
|
||||||
|
protected open val parseAuthorString = "Tác giả"
|
||||||
|
protected open val parseAlternativeNameString = "Tên khác"
|
||||||
|
protected open val parseAlternative2ndNameString = "Tên gốc"
|
||||||
|
protected open val parseStatusString = "Tình trạng"
|
||||||
|
protected open val parseStatusOngoingStringLowerCase = "đang tiến hành"
|
||||||
|
protected open val parseStatusOnHoldStringLowerCase = "tạm ngưng"
|
||||||
|
protected open val parseStatusCompletedStringLowerCase = "đã hoàn thành"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of words to be removed when parsing alternative names
|
||||||
|
*/
|
||||||
|
protected open val removeGenericWords = listOf("manhwa", "engsub")
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
|
||||||
override val client = network.cloudflareClient.newBuilder().apply {
|
override val client = network.cloudflareClient.newBuilder().apply {
|
||||||
@ -184,20 +197,23 @@ abstract class MyMangaCMS(
|
|||||||
)
|
)
|
||||||
title = document.select(".series-name").first()!!.text().trim()
|
title = document.select(".series-name").first()!!.text().trim()
|
||||||
|
|
||||||
var alternativeNames: String? = null
|
var alternativeNames: String = ""
|
||||||
document.select(".info-item").forEach {
|
document.select(".info-item").forEach {
|
||||||
val value = it.select(".info-value")
|
val value = it.select(".info-value")
|
||||||
when (it.select(".info-name").text().trim()) {
|
when (it.select(".info-name").text().trim()) {
|
||||||
"Tên khác:" -> alternativeNames = value.joinToString(", ") { name ->
|
"$parseAlternativeNameString:" -> alternativeNames += value.joinToString(", ") { name ->
|
||||||
name.text().trim()
|
removeGenericWords(name.text()).trim() + ", "
|
||||||
}
|
}
|
||||||
"Tác giả:" -> author = value.joinToString(", ") { auth ->
|
"$parseAlternative2ndNameString:" -> alternativeNames += value.joinToString(", ") { name ->
|
||||||
|
removeGenericWords(name.text()).trim() + ", "
|
||||||
|
}
|
||||||
|
"$parseAuthorString:" -> author = value.joinToString(", ") { auth ->
|
||||||
auth.text().trim()
|
auth.text().trim()
|
||||||
}
|
}
|
||||||
"Tình trạng:" -> status = when (value.first()!!.text().lowercase().trim()) {
|
"$parseStatusString:" -> status = when (value.first()!!.text().lowercase().trim()) {
|
||||||
"đang tiến hành" -> SManga.ONGOING
|
parseStatusOngoingStringLowerCase -> SManga.ONGOING
|
||||||
"tạm ngưng" -> SManga.ON_HIATUS
|
parseStatusOnHoldStringLowerCase -> SManga.ON_HIATUS
|
||||||
"đã hoàn thành" -> SManga.COMPLETED
|
parseStatusCompletedStringLowerCase -> SManga.COMPLETED
|
||||||
else -> SManga.UNKNOWN
|
else -> SManga.UNKNOWN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,8 +233,8 @@ abstract class MyMangaCMS(
|
|||||||
descElem.text().trim()
|
descElem.text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!alternativeNames.isNullOrEmpty()) {
|
if (alternativeNames.isNotEmpty()) {
|
||||||
description = "Tên khác: ${alternativeNames}\n\n" + description
|
description = "$parseAlternativeNameString: ${alternativeNames}\n\n" + description
|
||||||
}
|
}
|
||||||
|
|
||||||
genre = document.select("a[href*=the-loai] span.badge")
|
genre = document.select("a[href*=the-loai] span.badge")
|
||||||
@ -230,6 +246,14 @@ abstract class MyMangaCMS(
|
|||||||
.attr("style")
|
.attr("style")
|
||||||
.let { backgroundImageRegex.find(it)?.groups?.get(1)?.value }
|
.let { backgroundImageRegex.find(it)?.groups?.get(1)?.value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun removeGenericWords(name: String): String {
|
||||||
|
val locale = Locale.forLanguageTag(lang)
|
||||||
|
|
||||||
|
return name.split(' ')
|
||||||
|
.filterNot { word -> word.lowercase(locale) in removeGenericWords }
|
||||||
|
.joinToString(" ")
|
||||||
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region Chapter list
|
//region Chapter list
|
||||||
@ -294,30 +318,44 @@ abstract class MyMangaCMS(
|
|||||||
) : Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray(), state) {
|
) : Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray(), state) {
|
||||||
fun toUriPart() = vals[state].second
|
fun toUriPart() = vals[state].second
|
||||||
}
|
}
|
||||||
private class Status : Filter.Select<String>(
|
protected class Status(
|
||||||
"Tình trạng",
|
displayName: String = "Tình trạng",
|
||||||
|
statusAll: String = "Tất cả",
|
||||||
|
statusOngoing: String = "Đang tiến hành",
|
||||||
|
statusOnHold: String = "Tạm ngưng",
|
||||||
|
statusCompleted: String = "Hoàn thành",
|
||||||
|
) : Filter.Select<String>(
|
||||||
|
displayName,
|
||||||
arrayOf(
|
arrayOf(
|
||||||
"Tất cả",
|
statusAll,
|
||||||
"Đang tiến hành",
|
statusOngoing,
|
||||||
"Tạm ngưng",
|
statusOnHold,
|
||||||
"Hoàn thành",
|
statusCompleted,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
private class Sort : UriPartFilter(
|
protected class Sort(
|
||||||
"Sắp xếp",
|
displayName: String = "Sắp xếp",
|
||||||
|
sortAZ: String = "A-Z",
|
||||||
|
sortZA: String = "Z-A",
|
||||||
|
sortUpdate: String = "Mới cập nhật",
|
||||||
|
sortNew: String = "Truyện mới",
|
||||||
|
sortPopular: String = "Xem nhiều",
|
||||||
|
sortLike: String = "Được thích nhiều",
|
||||||
|
) : UriPartFilter(
|
||||||
|
displayName,
|
||||||
arrayOf(
|
arrayOf(
|
||||||
Pair("A-Z", "az"),
|
Pair(sortAZ, "az"),
|
||||||
Pair("Z-A", "za"),
|
Pair(sortZA, "za"),
|
||||||
Pair("Mới cập nhật", "update"),
|
Pair(sortUpdate, "update"),
|
||||||
Pair("Truyện mới", "new"),
|
Pair(sortNew, "new"),
|
||||||
Pair("Xem nhiều", "top"),
|
Pair(sortPopular, "top"),
|
||||||
Pair("Được thích nhiều", "like"),
|
Pair(sortLike, "like"),
|
||||||
),
|
),
|
||||||
4,
|
4,
|
||||||
)
|
)
|
||||||
open class Genre(name: String, val id: Int) : Filter.TriState(name)
|
open class Genre(name: String, val id: Int) : Filter.TriState(name)
|
||||||
private class Author : Filter.Text("Tác giả")
|
protected class Author(displayName: String = "Tác giả") : Filter.Text(displayName)
|
||||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Thể loại", genres)
|
protected class GenreList(genres: List<Genre>, displayName: String = "Thể loại") : Filter.Group<Genre>(displayName, genres)
|
||||||
|
|
||||||
override fun getFilterList(): FilterList = FilterList(
|
override fun getFilterList(): FilterList = FilterList(
|
||||||
Author(),
|
Author(),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.multisrc.mymangacms
|
package eu.kanade.tachiyomi.multisrc.mymangacms
|
||||||
|
|
||||||
|
import generator.ThemeSourceData.MultiLang
|
||||||
import generator.ThemeSourceData.SingleLang
|
import generator.ThemeSourceData.SingleLang
|
||||||
import generator.ThemeSourceGenerator
|
import generator.ThemeSourceGenerator
|
||||||
|
|
||||||
@ -9,15 +10,31 @@ class MyMangaCMSGenerator : ThemeSourceGenerator {
|
|||||||
|
|
||||||
override val themeClass = "MyMangaCMS"
|
override val themeClass = "MyMangaCMS"
|
||||||
|
|
||||||
override val baseVersionCode: Int = 1
|
override val baseVersionCode: Int = 2
|
||||||
|
|
||||||
override val sources = listOf(
|
override val sources = listOf(
|
||||||
SingleLang(
|
SingleLang(
|
||||||
"TruyenTranhLH",
|
"TruyenTranhLH",
|
||||||
"https://truyentranhlh.net",
|
"https://truyentranhlh.net",
|
||||||
"vi",
|
"vi",
|
||||||
|
isNsfw = true,
|
||||||
overrideVersionCode = 9,
|
overrideVersionCode = 9,
|
||||||
),
|
),
|
||||||
|
SingleLang(
|
||||||
|
"Manhwa18",
|
||||||
|
"https://manhwa18.com",
|
||||||
|
"en",
|
||||||
|
isNsfw = true,
|
||||||
|
overrideVersionCode = 9,
|
||||||
|
),
|
||||||
|
MultiLang(
|
||||||
|
"Manhwa18.net",
|
||||||
|
"https://manhwa18.net",
|
||||||
|
listOf("en"),
|
||||||
|
className = "Manhwa18Net",
|
||||||
|
isNsfw = true,
|
||||||
|
overrideVersionCode = 8,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|