Change and add Manga4Life
parent
ea4e69df19
commit
02581c9e2d
@ -0,0 +1,280 @@
|
||||
package org.koitharu.kotatsu.parsers.site.en
|
||||
|
||||
import okhttp3.Headers
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.PagedMangaParser
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
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.json.*
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.EnumSet
|
||||
|
||||
|
||||
@MangaSourceParser("MANGA4LIFE", "Manga4Life", "en")
|
||||
internal class Manga4Life(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.MANGA4LIFE, 0) {
|
||||
|
||||
override val sortOrders: Set<SortOrder> = EnumSet.of(SortOrder.ALPHABETICAL)
|
||||
|
||||
override val configKeyDomain = ConfigKey.Domain("manga4life.com")
|
||||
|
||||
override val headers: Headers = Headers.Builder()
|
||||
.add("User-Agent", UserAgents.CHROME_DESKTOP)
|
||||
.build()
|
||||
|
||||
|
||||
override suspend fun getListPage(
|
||||
page: Int,
|
||||
query: String?,
|
||||
tags: Set<MangaTag>?,
|
||||
sortOrder: SortOrder,
|
||||
): List<Manga> {
|
||||
|
||||
if (page > 1) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val url = buildString {
|
||||
append("https://$domain/search/")
|
||||
}
|
||||
val doc = webClient.httpGet(url).parseHtml()
|
||||
val json = JSONArray(
|
||||
doc.selectFirstOrThrow("script:containsData(MainFunction)").data()
|
||||
.substringAfter("vm.Directory = ").substringBefore("vm.GetIntValue").trim()
|
||||
.replace(";", " "),
|
||||
)
|
||||
|
||||
|
||||
val manga = ArrayList<Manga>()
|
||||
|
||||
for (i in 0 until json.length()) {
|
||||
val m = json.getJSONObject(i)
|
||||
val href = "/manga/" + m.getString("i")
|
||||
val imgUrl = "https://temp.compsci88.com/cover/" + m.getString("i") + ".jpg"
|
||||
if (!query.isNullOrEmpty()) {
|
||||
if (m.getString("i").contains(query.urlEncoded(), ignoreCase = true)) {
|
||||
manga.add(
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
title = m.getString("i").replace("-", " "),
|
||||
altTitle = null,
|
||||
url = href,
|
||||
publicUrl = href.toAbsoluteUrl(domain),
|
||||
rating = RATING_UNKNOWN,
|
||||
isNsfw = false,
|
||||
coverUrl = imgUrl,
|
||||
tags = emptySet(),
|
||||
state = null,
|
||||
author = null,
|
||||
source = source,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
} else if (!tags.isNullOrEmpty()) {
|
||||
|
||||
val a = m.getJSONArray("g").toString()
|
||||
var found = true
|
||||
tags.forEach {
|
||||
if (!a.contains(it.key, ignoreCase = true)) {
|
||||
found = false
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
manga.add(
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
title = m.getString("i").replace("-", " "),
|
||||
altTitle = null,
|
||||
url = href,
|
||||
publicUrl = href.toAbsoluteUrl(domain),
|
||||
rating = RATING_UNKNOWN,
|
||||
isNsfw = false,
|
||||
coverUrl = imgUrl,
|
||||
tags = emptySet(),
|
||||
state = null,
|
||||
author = null,
|
||||
source = source,
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
manga.add(
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
title = m.getString("i").replace("-", " "),
|
||||
altTitle = null,
|
||||
url = href,
|
||||
publicUrl = href.toAbsoluteUrl(domain),
|
||||
rating = RATING_UNKNOWN,
|
||||
isNsfw = false,
|
||||
coverUrl = imgUrl,
|
||||
tags = emptySet(),
|
||||
state = null,
|
||||
author = null,
|
||||
source = source,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return manga
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val doc = webClient.httpGet("https://$domain/search/").parseHtml()
|
||||
val tags = doc.selectFirstOrThrow("script:containsData(vm.AvailableFilters)").data()
|
||||
.substringAfter("\"Genre\" \t\t: [").substringBefore("]").replace("'", "").split(",")
|
||||
|
||||
return tags.mapNotNullToSet { tag ->
|
||||
MangaTag(
|
||||
key = tag,
|
||||
title = tag,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
|
||||
val chapter = JSONArray(
|
||||
JSONArray(
|
||||
doc.selectFirstOrThrow("script:containsData(MainFunction)").data()
|
||||
.substringAfter("vm.Chapters = ").substringBefore(";"),
|
||||
).toJSONList().reversed(),
|
||||
)
|
||||
|
||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:SS", sourceLocale)
|
||||
|
||||
return manga.copy(
|
||||
altTitle = null,
|
||||
state = when (doc.selectFirstOrThrow(".list-group-item:contains(Status:) a").text()) {
|
||||
"Ongoing (Scan)", "Ongoing (Publish)" -> MangaState.ONGOING
|
||||
"Complete (Scan)", "Complete (Publish)",
|
||||
"Cancelled (Scan)", "Cancelled (Publish)",
|
||||
"Discontinued (Scan)", "Discontinued (Publish)",
|
||||
"Hiatus (Scan)", "Hiatus (Publish)",
|
||||
-> MangaState.FINISHED
|
||||
|
||||
else -> null
|
||||
},
|
||||
tags = doc.select(".list-group-item:contains(Genre(s):) a").mapNotNullToSet { a ->
|
||||
MangaTag(
|
||||
key = a.attr("href").substringAfterLast("="),
|
||||
title = a.text(),
|
||||
source = source,
|
||||
)
|
||||
},
|
||||
author = doc.select(".list-group-item:contains(Author(s):) a").text(),
|
||||
description = doc.selectFirstOrThrow(".top-5.Content").text(),
|
||||
|
||||
chapters = chapter.mapJSONIndexed { i, j ->
|
||||
val indexChapter = j.getString("Chapter")!!
|
||||
val url = "/read-online/" + manga.url.substringAfter("/manga/") + chapterURLEncode(indexChapter)
|
||||
val name = j.getString("ChapterName").let {
|
||||
if (it.isNullOrEmpty() || it == "null") "${j.getString("Type")} ${
|
||||
chapterImage(
|
||||
indexChapter,
|
||||
true,
|
||||
)
|
||||
}" else it
|
||||
}
|
||||
val date = j.getString("Date")
|
||||
MangaChapter(
|
||||
id = generateUid(url),
|
||||
name = name,
|
||||
number = i + 1,
|
||||
url = url,
|
||||
scanlator = null,
|
||||
uploadDate = dateFormat.tryParse(date),
|
||||
branch = null,
|
||||
source = source,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun chapterURLEncode(e: String): String {
|
||||
var index = ""
|
||||
val t = e.substring(0, 1).toInt()
|
||||
if (1 != t) {
|
||||
index = "-index-$t"
|
||||
}
|
||||
val dgt = if (e.toInt() < 100100) {
|
||||
4
|
||||
} else if (e.toInt() < 101000) {
|
||||
3
|
||||
} else if (e.toInt() < 110000) {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
}
|
||||
val n = e.substring(dgt, e.length - 1)
|
||||
var suffix = ""
|
||||
val path = e.substring(e.length - 1).toInt()
|
||||
if (0 != path) {
|
||||
suffix = ".$path"
|
||||
}
|
||||
return "-chapter-$n$suffix$index.html"
|
||||
}
|
||||
|
||||
private val chapterImageRegex = Regex("""^0+""")
|
||||
|
||||
private fun chapterImage(e: String, cleanString: Boolean = false): String {
|
||||
// cleanString will result in an empty string if chapter number is 0, hence the else if below
|
||||
val a = e.substring(1, e.length - 1).let { if (cleanString) it.replace(chapterImageRegex, "") else it }
|
||||
// If b is not zero, indicates chapter has decimal numbering
|
||||
val b = e.substring(e.length - 1).toInt()
|
||||
return if (b == 0 && a.isNotEmpty()) {
|
||||
a
|
||||
} else if (b == 0 && a.isEmpty()) {
|
||||
"0"
|
||||
} else {
|
||||
"$a.$b"
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val fullUrl = chapter.url.toAbsoluteUrl(domain)
|
||||
val doc = webClient.httpGet(fullUrl).parseHtml()
|
||||
val script = doc.selectFirstOrThrow("script:containsData(MainFunction)").data()
|
||||
val curChapter = JSONObject(
|
||||
doc.selectFirstOrThrow("script:containsData(MainFunction)").data().substringAfter("vm.CurChapter = ")
|
||||
.substringBefore(";"),
|
||||
)
|
||||
val pageTotal = curChapter.getString("Page")!!.toInt()
|
||||
val host = "https://" +
|
||||
script
|
||||
.substringAfter("vm.CurPathName = \"", "")
|
||||
.substringBefore("\"")
|
||||
.also {
|
||||
if (it.isEmpty()) {
|
||||
throw Exception("Manga4Life is overloaded and blocking Tachiyomi right now. Wait for unblock.")
|
||||
}
|
||||
}
|
||||
val titleURI = script.substringAfter("vm.IndexName = \"").substringBefore("\"")
|
||||
val seasonURI = curChapter.getString("Directory")!!
|
||||
.let { if (it.isEmpty()) "" else "$it/" }
|
||||
val path = "$host/manga/$titleURI/$seasonURI"
|
||||
val chNum = chapterImage(curChapter.getString("Chapter")!!)
|
||||
|
||||
return IntRange(1, pageTotal).mapIndexed { i, _ ->
|
||||
val imageNum = (i + 1).toString().let { "000$it" }.let { it.substring(it.length - 3) }
|
||||
val url = "$path$chNum-$imageNum.png"
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
preview = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue