Change and add Manga4Life

pull/235/head
devi 3 years ago
parent ea4e69df19
commit 02581c9e2d

@ -86,7 +86,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
val doc = webClient.httpGet("https://$domain/popular-comic").parseHtml()
return doc.select("li.tag-item a").mapNotNullToSet { a ->
MangaTag(
key = a.attr("href").substringAfterLast("/"),
key = a.attr("href").substringAfterLast('/'),
title = a.text(),
source = source,
)
@ -95,6 +95,7 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val dateFormat = SimpleDateFormat("MM/dd/yy", sourceLocale)
return manga.copy(
altTitle = doc.selectFirstOrThrow("dt.movie-dt:contains(Alternate name:) + dd").text(),
state = when (doc.selectFirstOrThrow("dt.movie-dt:contains(Status:) + dd a").text()) {
@ -113,16 +114,14 @@ internal class ComicExtra(context: MangaLoaderContext) : PagedMangaParser(contex
description = doc.getElementById("film-content")?.text(),
chapters = doc.requireElementById("list").select("tr")
.mapChapters(reversed = true) { i, tr ->
val a = tr.selectFirstOrThrow("a")
val url = a.attrAsRelativeUrl("href") + "/full"
val name = a.text()
val dateText = tr.select("td").last()?.text()
val dateFormat = SimpleDateFormat("MM/dd/yy", sourceLocale)
MangaChapter(
id = generateUid(url),
name = name,
number = i,
number = i + 1,
url = url,
scanlator = null,
uploadDate = dateFormat.tryParse(dateText),

@ -126,13 +126,12 @@ internal class Comicastle(context: MangaLoaderContext) : PagedMangaParser(contex
description = root.getElementById("comic-desc")?.text(),
chapters = root.select("div.card-body > .table-responsive tr a")
.mapChapters { i, a ->
val url = a.attrAsRelativeUrl("href")
val name = a.text()
MangaChapter(
id = generateUid(url),
name = name,
number = i,
number = i + 1,
url = url,
scanlator = null,
uploadDate = 0,

@ -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,
)
}
}
}

@ -130,7 +130,7 @@ internal class CoffeeMangaTop(context: MangaLoaderContext) :
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val urlarray = doc.select("p#arraydata").text().split(",").toTypedArray()
val urlarray = doc.select("p#arraydata").text().split(',').toTypedArray()
return urlarray.map { url ->
MangaPage(
id = generateUid(url),

@ -90,8 +90,8 @@ internal class MangaPure(context: MangaLoaderContext) :
author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText(),
state = when (summary?.selectFirst(".mg_status")?.selectFirst(".summary-content")?.ownText()?.trim()
?.lowercase()) {
"Ongoing" -> MangaState.ONGOING
"Completed " -> MangaState.FINISHED
"ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED
else -> null
},
source = source,

@ -42,9 +42,9 @@ internal class Mangakakalot(context: MangaLoaderContext) :
append("$listUrl/")
when (sortOrder) {
SortOrder.POPULARITY -> append("?type=topview")
SortOrder.UPDATED -> append("")
SortOrder.UPDATED -> append("?type=latest")
SortOrder.NEWEST -> append("?type=newest")
else -> append("")
else -> append("?type=latest")
}
if (!tags.isNullOrEmpty()) {
append("&category=")

@ -68,9 +68,9 @@ internal class RealmScans(context: MangaLoaderContext) :
val selectTag = docs.select(".wd-full .mgen > a")
val tags = selectTag.mapNotNullToSet { tagMap[it.text()] }
val mangaState = docs.selectFirst(".bs-status")?.let {
when (it.text()) {
when (it.text().lowercase()) {
"ongoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED
"completed" -> MangaState.FINISHED
else -> null
}
}

@ -86,7 +86,7 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context,
val doc = webClient.httpGet("https://$domain/lista-de-generos-de-manga/").parseHtml()
return doc.select(".genres_page a").mapNotNullToSet { a ->
MangaTag(
key = a.attr("href").removeSuffix("/").substringAfterLast("/"),
key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
title = a.text(),
source = source,
)
@ -100,7 +100,7 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context,
state = null,
tags = doc.select("div.serie-infos li:contains(Categorias:) a").mapNotNullToSet { a ->
MangaTag(
key = a.attr("href").removeSuffix("/").substringAfterLast("/"),
key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
title = a.text(),
source = source,
)
@ -115,7 +115,7 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context,
MangaChapter(
id = generateUid(url),
name = name,
number = i,
number = i + 1,
url = url,
scanlator = null,
uploadDate = 0,
@ -130,7 +130,7 @@ internal class BrMangas(context: MangaLoaderContext) : PagedMangaParser(context,
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val scriptData =
doc.selectFirstOrThrow("script:containsData(imageArray)").data().substringAfter("[").substringBefore("]")
doc.selectFirstOrThrow("script:containsData(imageArray)").data().substringAfter('[').substringBefore(']')
.split(",")
return scriptData.map { data ->
val url = data.replace("\\\"", "").replace("\\/", "/")

@ -104,7 +104,7 @@ internal abstract class SinmhParser(
override suspend fun getTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml()
return doc.select(".filter-item:contains(按剧情) li a:not(.active)").mapNotNullToSet { a ->
val href = a.attr("href").removeSuffix("/").substringAfterLast("/")
val href = a.attr("href").removeSuffix('/').substringAfterLast('/')
MangaTag(
key = href,
title = a.text(),
@ -136,7 +136,7 @@ internal abstract class SinmhParser(
manga.copy(
tags = doc.body().select(selectGenre).mapNotNullToSet { a ->
MangaTag(
key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
key = a.attr("href").removeSuffix('/').substringAfterLast('/'),
title = a.text().toTitleCase(),
source = source,
)
@ -152,7 +152,7 @@ internal abstract class SinmhParser(
protected open suspend fun getChapters(manga: Manga, doc: Document): List<MangaChapter> {
return doc.body().select(selectChapter).mapChapters { i, li ->
val href = li.selectFirstOrThrow("a").attrAsRelativeUrlOrNull("href") ?: li.parseFailed("Link is missing")
val href = li.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val name = li.selectFirstOrThrow("a").text()
MangaChapter(
id = generateUid(href),

Loading…
Cancel
Save