|
|
|
@ -6,7 +6,6 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
|
|
|
|
import org.koitharu.kotatsu.parsers.PagedMangaParser
|
|
|
|
import org.koitharu.kotatsu.parsers.PagedMangaParser
|
|
|
|
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
|
|
|
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
|
|
|
import org.koitharu.kotatsu.parsers.model.*
|
|
|
|
import org.koitharu.kotatsu.parsers.model.*
|
|
|
|
import org.koitharu.kotatsu.parsers.network.UserAgents
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.*
|
|
|
|
import org.koitharu.kotatsu.parsers.util.*
|
|
|
|
import java.util.*
|
|
|
|
import java.util.*
|
|
|
|
|
|
|
|
|
|
|
|
@ -52,18 +51,20 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context,
|
|
|
|
|
|
|
|
|
|
|
|
if (page > 1) {
|
|
|
|
if (page > 1) {
|
|
|
|
append("&page=")
|
|
|
|
append("&page=")
|
|
|
|
append(page.toString())
|
|
|
|
append(page)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
append("&sort=")
|
|
|
|
append("&sort=")
|
|
|
|
append(when (order) {
|
|
|
|
append(
|
|
|
|
|
|
|
|
when (order) {
|
|
|
|
SortOrder.POPULARITY -> "-views"
|
|
|
|
SortOrder.POPULARITY -> "-views"
|
|
|
|
SortOrder.UPDATED -> "-updated_at"
|
|
|
|
SortOrder.UPDATED -> "-updated_at"
|
|
|
|
SortOrder.NEWEST -> "-created_at"
|
|
|
|
SortOrder.NEWEST -> "-created_at"
|
|
|
|
SortOrder.ALPHABETICAL -> "name"
|
|
|
|
SortOrder.ALPHABETICAL -> "name"
|
|
|
|
SortOrder.ALPHABETICAL_DESC -> "-name"
|
|
|
|
SortOrder.ALPHABETICAL_DESC -> "-name"
|
|
|
|
else -> "-updated_at"
|
|
|
|
else -> "-updated_at"
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
filter.tags.isNotEmpty() -> {
|
|
|
|
filter.tags.isNotEmpty() -> {
|
|
|
|
@ -78,14 +79,16 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context,
|
|
|
|
else -> {
|
|
|
|
else -> {
|
|
|
|
append("/danh-sach")
|
|
|
|
append("/danh-sach")
|
|
|
|
append("?sort=")
|
|
|
|
append("?sort=")
|
|
|
|
append(when (order) {
|
|
|
|
append(
|
|
|
|
|
|
|
|
when (order) {
|
|
|
|
SortOrder.POPULARITY -> "-views"
|
|
|
|
SortOrder.POPULARITY -> "-views"
|
|
|
|
SortOrder.UPDATED -> "-updated_at"
|
|
|
|
SortOrder.UPDATED -> "-updated_at"
|
|
|
|
SortOrder.NEWEST -> "-created_at"
|
|
|
|
SortOrder.NEWEST -> "-created_at"
|
|
|
|
SortOrder.ALPHABETICAL -> "name"
|
|
|
|
SortOrder.ALPHABETICAL -> "name"
|
|
|
|
SortOrder.ALPHABETICAL_DESC -> "-name"
|
|
|
|
SortOrder.ALPHABETICAL_DESC -> "-name"
|
|
|
|
else -> "-updated_at"
|
|
|
|
else -> "-updated_at"
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
)
|
|
|
|
append("&page=")
|
|
|
|
append("&page=")
|
|
|
|
append(page)
|
|
|
|
append(page)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -120,12 +123,11 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context,
|
|
|
|
val doc = webClient.httpGet(url).parseHtml()
|
|
|
|
val doc = webClient.httpGet(url).parseHtml()
|
|
|
|
|
|
|
|
|
|
|
|
return doc.select("div.grid div.relative").map { div ->
|
|
|
|
return doc.select("div.grid div.relative").map { div ->
|
|
|
|
val href = div.selectFirst("a[href^=/truyen/]")?.attr("href")
|
|
|
|
val href = div.selectFirst("a[href^=/truyen/]")?.attrOrNull("href")
|
|
|
|
?: div.parseFailed("Không thể tìm thấy nguồn ảnh của Manga này!")
|
|
|
|
?: div.parseFailed("Không thể tìm thấy nguồn ảnh của Manga này!")
|
|
|
|
val coverStyle = div.select("div.cover").attr("data-bg")
|
|
|
|
val coverUrl = div.selectFirstOrThrow("div.cover").let {
|
|
|
|
val coverUrl = coverStyle.takeIf { it.isNotEmpty() } ?: div.select("div.cover").attr("style")
|
|
|
|
it.attrOrNull("data-bg") ?: it.attr("style").cssUrl()?.replace("s3.lxmanga.top", domain)
|
|
|
|
.substringAfter("url('").substringBefore("')")
|
|
|
|
}.orEmpty()
|
|
|
|
.replace("s3.lxmanga.top", domain)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Manga(
|
|
|
|
Manga(
|
|
|
|
id = generateUid(href),
|
|
|
|
id = generateUid(href),
|
|
|
|
@ -148,27 +150,27 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context,
|
|
|
|
val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
|
|
|
val root = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
|
|
|
|
|
|
|
|
|
|
|
return manga.copy(
|
|
|
|
return manga.copy(
|
|
|
|
altTitle = root.select("div.grow div:contains(Tên khác) span").lastOrNull()?.text(),
|
|
|
|
altTitle = root.selectLast("div.grow div:contains(Tên khác) span")?.textOrNull(),
|
|
|
|
state = when (root.select("div.mt-2:contains(Tình trạng) span.text-blue-500").firstOrNull()?.text()?.trim()) {
|
|
|
|
state = when (root.selectFirst("div.mt-2:contains(Tình trạng) span.text-blue-500")?.text()) {
|
|
|
|
"Đang tiến hành" -> MangaState.ONGOING
|
|
|
|
"Đang tiến hành" -> MangaState.ONGOING
|
|
|
|
"Đã hoàn thành" -> MangaState.FINISHED
|
|
|
|
"Đã hoàn thành" -> MangaState.FINISHED
|
|
|
|
else -> null
|
|
|
|
else -> null
|
|
|
|
},
|
|
|
|
},
|
|
|
|
tags = root.select("div.mt-2:contains(Thể loại) a.bg-gray-500").mapNotNullToSet { a ->
|
|
|
|
tags = root.select("div.mt-2:contains(Thể loại) a.bg-gray-500").mapToSet { a ->
|
|
|
|
MangaTag(
|
|
|
|
MangaTag(
|
|
|
|
key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
|
|
|
|
key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
|
|
|
|
title = a.text().trim(),
|
|
|
|
title = a.text(),
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
author = root.select("div.mt-2:contains(Tác giả) span a").firstOrNull()?.text()?.trim(),
|
|
|
|
author = root.selectFirst("div.mt-2:contains(Tác giả) span a")?.textOrNull(),
|
|
|
|
description = root.select("meta[name=description]").firstOrNull()?.attr("content")?.trim(),
|
|
|
|
description = root.selectFirst("meta[name=description]")?.attrOrNull("content"),
|
|
|
|
chapters = root.select("div.justify-between ul.overflow-y-auto.overflow-x-hidden a")
|
|
|
|
chapters = root.select("div.justify-between ul.overflow-y-auto.overflow-x-hidden a")
|
|
|
|
.mapChapters(reversed = true) { i, a ->
|
|
|
|
.mapChapters(reversed = true) { i, a ->
|
|
|
|
val href = a.attr("href")
|
|
|
|
val href = a.attrAsRelativeUrl("href")
|
|
|
|
val name = a.selectFirst("span.text-ellipsis")?.text() ?: ""
|
|
|
|
val name = a.selectFirst("span.text-ellipsis")?.text().orEmpty()
|
|
|
|
val dateText = a.parent()?.selectFirst("span.timeago")?.attr("datetime") ?: ""
|
|
|
|
val dateText = a.parent()?.selectFirst("span.timeago")?.attr("datetime").orEmpty()
|
|
|
|
val scanlator = root.select("div.mt-2:contains(Nhóm dịch) span a").firstOrNull()?.text()?.trim()
|
|
|
|
val scanlator = root.selectFirst("div.mt-2:contains(Nhóm dịch) span a")?.textOrNull()
|
|
|
|
|
|
|
|
|
|
|
|
MangaChapter(
|
|
|
|
MangaChapter(
|
|
|
|
id = generateUid(href),
|
|
|
|
id = generateUid(href),
|
|
|
|
@ -192,14 +194,17 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context,
|
|
|
|
.mapNotNull { div ->
|
|
|
|
.mapNotNull { div ->
|
|
|
|
val url = div.attr("data-src")
|
|
|
|
val url = div.attr("data-src")
|
|
|
|
if (url.endsWith(".jpg", ignoreCase = true) ||
|
|
|
|
if (url.endsWith(".jpg", ignoreCase = true) ||
|
|
|
|
url.endsWith(".png", ignoreCase = true)) {
|
|
|
|
url.endsWith(".png", ignoreCase = true)
|
|
|
|
|
|
|
|
) {
|
|
|
|
MangaPage(
|
|
|
|
MangaPage(
|
|
|
|
id = generateUid(url),
|
|
|
|
id = generateUid(url),
|
|
|
|
url = url,
|
|
|
|
url = url,
|
|
|
|
preview = null,
|
|
|
|
preview = null,
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
} else null
|
|
|
|
} else {
|
|
|
|
|
|
|
|
null
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -274,14 +279,13 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context,
|
|
|
|
MangaTag("Xúc Tua", "xuc-tua", source),
|
|
|
|
MangaTag("Xúc Tua", "xuc-tua", source),
|
|
|
|
MangaTag("Y Tá", "y-ta", source),
|
|
|
|
MangaTag("Y Tá", "y-ta", source),
|
|
|
|
MangaTag("Yaoi", "yaoi", source),
|
|
|
|
MangaTag("Yaoi", "yaoi", source),
|
|
|
|
MangaTag("Yuri", "yuri", source)
|
|
|
|
MangaTag("Yuri", "yuri", source),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
private fun parseDateTime(dateStr: String): Long {
|
|
|
|
private fun parseDateTime(dateStr: String): Long = runCatching {
|
|
|
|
return try {
|
|
|
|
val parts = dateStr.split(' ')
|
|
|
|
val parts = dateStr.split(" ")
|
|
|
|
val dateParts = parts[0].split('-')
|
|
|
|
val dateParts = parts[0].split("-")
|
|
|
|
val timeParts = parts[1].split(':')
|
|
|
|
val timeParts = parts[1].split(":")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val calendar = Calendar.getInstance()
|
|
|
|
val calendar = Calendar.getInstance()
|
|
|
|
calendar.set(
|
|
|
|
calendar.set(
|
|
|
|
@ -290,11 +294,8 @@ internal class LxManga(context: MangaLoaderContext) : PagedMangaParser(context,
|
|
|
|
dateParts[2].toInt(),
|
|
|
|
dateParts[2].toInt(),
|
|
|
|
timeParts[0].toInt(),
|
|
|
|
timeParts[0].toInt(),
|
|
|
|
timeParts[1].toInt(),
|
|
|
|
timeParts[1].toInt(),
|
|
|
|
timeParts[2].toInt()
|
|
|
|
timeParts[2].toInt(),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
calendar.timeInMillis
|
|
|
|
calendar.timeInMillis
|
|
|
|
} catch (e: Exception) {
|
|
|
|
}.getOrDefault(0L)
|
|
|
|
System.currentTimeMillis()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|