Fixes batch
This commit is contained in:
@@ -71,4 +71,15 @@ class MangaChapter(
|
||||
override fun toString(): String {
|
||||
return "MangaChapter($id - #$number [$url] - $source)"
|
||||
}
|
||||
|
||||
internal fun copy(number: Int) = MangaChapter(
|
||||
id = id,
|
||||
name = name,
|
||||
number = number,
|
||||
url = url,
|
||||
scanlator = scanlator,
|
||||
uploadDate = uploadDate,
|
||||
branch = branch,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,6 +417,4 @@ internal class ExHentaiParser(
|
||||
}
|
||||
return joiner.complete().takeUnless { it.isEmpty() }
|
||||
}
|
||||
|
||||
private fun String.isNumeric() = all { c -> c.isDigit() }
|
||||
}
|
||||
|
||||
@@ -155,28 +155,13 @@ internal abstract class GroupleParser(
|
||||
val scripts = doc.select("script")
|
||||
for (script in scripts) {
|
||||
val data = script.html()
|
||||
val pos = data.indexOf("rm_h.readerInit( 0,")
|
||||
if (pos == -1) {
|
||||
continue
|
||||
var pos = data.indexOf("rm_h.readerDoInit(")
|
||||
if (pos != -1) {
|
||||
parsePagesNew(data, pos)?.let { return it } ?: continue
|
||||
}
|
||||
val json = data.substring(pos).substringAfter('(').substringBefore('\n').substringBeforeLast(')')
|
||||
if (json.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
val ja = JSONArray("[$json]")
|
||||
val pages = ja.getJSONArray(1)
|
||||
val servers = ja.getJSONArray(3).mapJSON { it.getString("path") }
|
||||
val serversStr = servers.joinToString("|")
|
||||
return (0 until pages.length()).map { i ->
|
||||
val page = pages.getJSONArray(i)
|
||||
val primaryServer = page.getString(0)
|
||||
val url = page.getString(2)
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = "$primaryServer|$serversStr|$url",
|
||||
preview = null,
|
||||
source = source,
|
||||
)
|
||||
pos = data.indexOf("rm_h.readerInit( 0,")
|
||||
if (pos != -1) {
|
||||
parsePagesOld(data, pos)?.let { return it } ?: continue
|
||||
}
|
||||
}
|
||||
doc.parseFailed("Pages list not found at ${chapter.url}")
|
||||
@@ -373,4 +358,48 @@ internal abstract class GroupleParser(
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
|
||||
private fun parsePagesNew(data: String, pos: Int): List<MangaPage>? {
|
||||
val json = data.substring(pos).substringAfter('(').substringBefore('\n').substringBeforeLast(')')
|
||||
if (json.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
val ja = JSONArray("[$json]")
|
||||
val pages = ja.getJSONArray(0)
|
||||
val servers = ja.getJSONArray(2).mapJSON { it.getString("path") }
|
||||
val serversStr = servers.joinToString("|")
|
||||
return (0 until pages.length()).map { i ->
|
||||
val page = pages.getJSONArray(i)
|
||||
val primaryServer = page.getString(2)
|
||||
val url = page.getString(1)
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = "$primaryServer|$serversStr|$url",
|
||||
preview = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parsePagesOld(data: String, pos: Int): List<MangaPage>? {
|
||||
val json = data.substring(pos).substringAfter('(').substringBefore('\n').substringBeforeLast(')')
|
||||
if (json.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
val ja = JSONArray("[$json]")
|
||||
val pages = ja.getJSONArray(1)
|
||||
val servers = ja.getJSONArray(3).mapJSON { it.getString("path") }
|
||||
val serversStr = servers.joinToString("|")
|
||||
return (0 until pages.length()).map { i ->
|
||||
val page = pages.getJSONArray(i)
|
||||
val primaryServer = page.getString(0)
|
||||
val url = page.getString(2)
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = "$primaryServer|$serversStr|$url",
|
||||
preview = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,8 @@ internal class MintMangaParser(
|
||||
) : GroupleParser(context, MangaSource.MINTMANGA, 2) {
|
||||
|
||||
override val configKeyDomain = ConfigKey.Domain(
|
||||
"23.mintmanga.live",
|
||||
"24.mintmanga.one",
|
||||
"mintmanga.live",
|
||||
"mintmanga.com",
|
||||
"m.mintmanga.live",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.koitharu.kotatsu.parsers.site.ru.multichan
|
||||
|
||||
import okhttp3.HttpUrl
|
||||
import org.jsoup.internal.StringUtil
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaParser
|
||||
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
|
||||
@@ -22,21 +23,17 @@ internal abstract class ChanParser(
|
||||
SortOrder.RATING,
|
||||
)
|
||||
|
||||
override val isTagsExclusionSupported: Boolean = true
|
||||
|
||||
override val authUrl: String
|
||||
get() = "https://${domain}"
|
||||
|
||||
override val isAuthorized: Boolean
|
||||
get() = context.cookieJar.getCookies(domain).any { it.name == "dle_user_id" }
|
||||
|
||||
override suspend fun getList(
|
||||
offset: Int,
|
||||
query: String?,
|
||||
tags: Set<MangaTag>?,
|
||||
tagsExclude: Set<MangaTag>?,
|
||||
sortOrder: SortOrder,
|
||||
): List<Manga> {
|
||||
override suspend fun getList(offset: Int, filter: MangaListFilter?): List<Manga> {
|
||||
val domain = domain
|
||||
val doc = webClient.httpGet(buildUrl(offset, query, tags, sortOrder)).parseHtml()
|
||||
val doc = webClient.httpGet(buildUrl(offset, filter)).parseHtml()
|
||||
val root = doc.body().selectFirst("div.main_fon")?.getElementById("content")
|
||||
?: doc.parseFailed("Cannot find root")
|
||||
return root.select("div.content_row").mapNotNull { row ->
|
||||
@@ -180,48 +177,56 @@ internal abstract class ChanParser(
|
||||
|
||||
protected open fun buildUrl(
|
||||
offset: Int,
|
||||
query: String?,
|
||||
tags: Set<MangaTag>?,
|
||||
sortOrder: SortOrder,
|
||||
filter: MangaListFilter?,
|
||||
): HttpUrl {
|
||||
val builder = urlBuilder()
|
||||
builder.addQueryParameter("offset", offset.toString())
|
||||
when {
|
||||
!query.isNullOrEmpty() -> {
|
||||
when (filter) {
|
||||
is MangaListFilter.Search -> {
|
||||
builder.addQueryParameter("do", "search")
|
||||
builder.addQueryParameter("subaction", "search")
|
||||
builder.addQueryParameter("search_start", ((offset / 40) + 1).toString())
|
||||
builder.addQueryParameter("full_search", "0")
|
||||
builder.addQueryParameter("result_from", (offset + 1).toString())
|
||||
builder.addQueryParameter("result_num", "40")
|
||||
builder.addQueryParameter("story", query)
|
||||
builder.addQueryParameter("story", filter.query)
|
||||
builder.addQueryParameter("need_sort_date", "false")
|
||||
}
|
||||
|
||||
!tags.isNullOrEmpty() -> {
|
||||
builder.addPathSegment("tags")
|
||||
builder.addPathSegment(tags.joinToString("+") { it.key })
|
||||
builder.addQueryParameter(
|
||||
"n",
|
||||
when (sortOrder) {
|
||||
SortOrder.RATING,
|
||||
SortOrder.POPULARITY,
|
||||
-> "favdesc"
|
||||
is MangaListFilter.Advanced -> {
|
||||
if (filter.tags.isNotEmpty() || filter.tagsExclude.isNotEmpty()) {
|
||||
builder.addPathSegment("tags")
|
||||
val joiner = StringUtil.StringJoiner("+")
|
||||
filter.tags.forEach { joiner.add(it.key) }
|
||||
filter.tagsExclude.forEach { joiner.add("-"); joiner.append(it.key) }
|
||||
builder.addPathSegment(joiner.complete())
|
||||
builder.addQueryParameter(
|
||||
"n",
|
||||
when (filter.sortOrder) {
|
||||
SortOrder.RATING,
|
||||
SortOrder.POPULARITY,
|
||||
-> "favdesc"
|
||||
|
||||
SortOrder.ALPHABETICAL -> "abcasc"
|
||||
else -> "" // SortOrder.NEWEST
|
||||
},
|
||||
)
|
||||
SortOrder.ALPHABETICAL -> "abcasc"
|
||||
else -> "" // SortOrder.NEWEST
|
||||
},
|
||||
)
|
||||
} else {
|
||||
when (filter.sortOrder) {
|
||||
SortOrder.POPULARITY -> builder.addPathSegment("mostviews")
|
||||
SortOrder.ALPHABETICAL -> builder.addPathSegment("catalog")
|
||||
SortOrder.RATING -> builder.addPathSegment("mostfavorites")
|
||||
else -> { // SortOrder.NEWEST
|
||||
builder.addPathSegment("manga")
|
||||
builder.addPathSegment("new")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> when (sortOrder) {
|
||||
SortOrder.POPULARITY -> builder.addPathSegment("mostviews")
|
||||
SortOrder.ALPHABETICAL -> builder.addPathSegment("catalog")
|
||||
SortOrder.RATING -> builder.addPathSegment("mostfavorites")
|
||||
else -> { // SortOrder.NEWEST
|
||||
builder.addPathSegment("manga")
|
||||
builder.addPathSegment("new")
|
||||
}
|
||||
null -> {
|
||||
builder.addPathSegment("manga")
|
||||
builder.addPathSegment("new")
|
||||
}
|
||||
}
|
||||
return builder.build()
|
||||
|
||||
@@ -58,10 +58,10 @@ internal class HenChanParser(context: MangaLoaderContext) : ChanParser(context,
|
||||
)
|
||||
}
|
||||
|
||||
override fun buildUrl(offset: Int, query: String?, tags: Set<MangaTag>?, sortOrder: SortOrder): HttpUrl {
|
||||
if (query.isNullOrEmpty() && tags.isNullOrEmpty()) {
|
||||
override fun buildUrl(offset: Int, filter: MangaListFilter?): HttpUrl = when {
|
||||
filter is MangaListFilter.Advanced && filter.tags.isEmpty() && filter.tagsExclude.isEmpty() -> {
|
||||
val builder = urlBuilder().addQueryParameter("offset", offset.toString())
|
||||
when (sortOrder) {
|
||||
when (filter.sortOrder) {
|
||||
SortOrder.POPULARITY -> {
|
||||
builder.addPathSegment("mostviews")
|
||||
builder.addQueryParameter("sort", "manga")
|
||||
@@ -77,8 +77,18 @@ internal class HenChanParser(context: MangaLoaderContext) : ChanParser(context,
|
||||
builder.addPathSegment("newest")
|
||||
}
|
||||
}
|
||||
return builder.build()
|
||||
builder.build()
|
||||
}
|
||||
|
||||
filter == null -> {
|
||||
val builder = urlBuilder().addQueryParameter("offset", offset.toString())
|
||||
builder.addPathSegment("manga")
|
||||
builder.addPathSegment("newest")
|
||||
builder.build()
|
||||
}
|
||||
|
||||
else -> {
|
||||
super.buildUrl(offset, filter)
|
||||
}
|
||||
return super.buildUrl(offset, query, tags, sortOrder)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,9 +178,11 @@ internal abstract class WpComicsParser(
|
||||
val tagItems = doc.select("div.genre-item")
|
||||
val result = ArrayMap<String, MangaTag>(tagItems.size)
|
||||
for (item in tagItems) {
|
||||
val title = item.text().trim()
|
||||
val title = item.text()
|
||||
val key = item.select("span[data-id]").attr("data-id")
|
||||
result[title] = MangaTag(title = title, key = key, source = source)
|
||||
if (key.isNotEmpty() && title.isNotEmpty()) {
|
||||
result[title] = MangaTag(title = title, key = key, source = source)
|
||||
}
|
||||
}
|
||||
tagCache = result
|
||||
result
|
||||
@@ -216,7 +218,7 @@ internal abstract class WpComicsParser(
|
||||
|
||||
|
||||
protected open val selectDate = "div.col-xs-4"
|
||||
protected open val selectChapter = "div#nt_listchapter li:not(.heading)"
|
||||
protected open val selectChapter = "div#nt_listchapter li .chapter"
|
||||
|
||||
protected open suspend fun getChapters(doc: Document): List<MangaChapter> {
|
||||
return doc.body().select(selectChapter).mapChapters(reversed = true) { i, li ->
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package org.koitharu.kotatsu.parsers.site.wpcomics.en
|
||||
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import org.jsoup.nodes.Document
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
import java.util.EnumSet
|
||||
import java.util.*
|
||||
|
||||
@MangaSourceParser("XOXOCOMICS", "XoxoComics", "en", ContentType.COMICS)
|
||||
internal class XoxoComics(context: MangaLoaderContext) :
|
||||
@@ -146,6 +148,29 @@ internal class XoxoComics(context: MangaLoaderContext) :
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getChapters(doc: Document): List<MangaChapter> {
|
||||
val pages = doc.select("ul.pagination > li:not(.active)")
|
||||
return if (pages.size <= 1) {
|
||||
super.getChapters(doc)
|
||||
} else {
|
||||
val list = coroutineScope {
|
||||
pages.mapNotNull { page ->
|
||||
val a = page.selectFirst("a") ?: return@mapNotNull null
|
||||
if (a.text().isNumeric()) {
|
||||
val href = a.attrAsAbsoluteUrl("href")
|
||||
async {
|
||||
super.getChapters(webClient.httpGet(href).parseHtml()).asReversed()
|
||||
}
|
||||
} else {
|
||||
null // TODO support pagination with overflow
|
||||
}
|
||||
}.awaitAll().flattenTo(ArrayList())
|
||||
}
|
||||
list.addAll(super.getChapters(doc).asReversed())
|
||||
list.reverse()
|
||||
list.mapIndexed { i, x -> x.copy(number = i + 1) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val fullUrl = chapter.url.toAbsoluteUrl(domain) + "/all"
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
|
||||
internal class NetTruyen(context: MangaLoaderContext) :
|
||||
WpComicsParser(context, MangaSource.NETTRUYEN, "www.nettruyenlive.com", 36) {
|
||||
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain(
|
||||
"www.nettruyenaz.com",
|
||||
"www.nettruyenlive.com",
|
||||
"www.nettruyenio.com",
|
||||
"www.nettruyento.com",
|
||||
|
||||
@@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.wpcomics.WpComicsParser
|
||||
|
||||
@MangaSourceParser("NHATTRUYENMIN", "NhattruyenPlus", "vi")
|
||||
internal class Nhattruyenmin(context: MangaLoaderContext) :
|
||||
WpComicsParser(context, MangaSource.NHATTRUYENMIN, "nhattruyenplus.com")
|
||||
WpComicsParser(context, MangaSource.NHATTRUYENMIN, "nhattruyenmax.com")
|
||||
|
||||
@@ -240,3 +240,5 @@ inline fun <T> Appendable.appendAll(
|
||||
append(transform(item))
|
||||
}
|
||||
}
|
||||
|
||||
fun String.isNumeric() = all { c -> c.isDigit() }
|
||||
|
||||
Reference in New Issue
Block a user