Use relative urls for mangas and change id generation algorythm
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
package org.koitharu.kotatsu.core.parser
|
||||
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
@@ -13,27 +12,60 @@ abstract class RemoteMangaRepository(
|
||||
|
||||
protected abstract val source: MangaSource
|
||||
|
||||
protected val conf by lazy {
|
||||
protected abstract val defaultDomain: String
|
||||
|
||||
private val conf by lazy {
|
||||
loaderContext.getSettings(source)
|
||||
}
|
||||
|
||||
override val sortOrders: Set<SortOrder> get() = emptySet()
|
||||
|
||||
override suspend fun getPageUrl(page: MangaPage): String = page.url
|
||||
override suspend fun getPageUrl(page: MangaPage): String = page.url.withDomain()
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> = emptySet()
|
||||
|
||||
abstract fun onCreatePreferences(): Set<String>
|
||||
|
||||
protected fun generateUid(url: String): Long {
|
||||
val uri = url.toHttpUrl()
|
||||
val x = source.name.hashCode()
|
||||
val y = "${uri.encodedPath}?${uri.query}".hashCode()
|
||||
return (x.toLong() shl 32) or (y.toLong() and 0xffffffffL)
|
||||
protected fun getDomain() = conf.getDomain(defaultDomain)
|
||||
|
||||
protected fun String.withDomain() = when {
|
||||
this.startsWith("//") -> buildString {
|
||||
append("http")
|
||||
if (conf.isUseSsl(true)) {
|
||||
append('s')
|
||||
}
|
||||
append(":")
|
||||
append(this@withDomain)
|
||||
}
|
||||
this.startsWith("/") -> buildString {
|
||||
append("http")
|
||||
if (conf.isUseSsl(true)) {
|
||||
append('s')
|
||||
}
|
||||
append("://")
|
||||
append(conf.getDomain(defaultDomain))
|
||||
append(this@withDomain)
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
|
||||
protected fun generateUid(id: Int): Long {
|
||||
val x = source.name.hashCode()
|
||||
return (x.toLong() shl 32) or (id.toLong() and 0xffffffffL)
|
||||
protected fun generateUid(url: String): Long {
|
||||
var h = 1125899906842597L
|
||||
source.name.forEach { c ->
|
||||
h = 31 * h + c.toLong()
|
||||
}
|
||||
url.forEach { c ->
|
||||
h = 31 * h + c.toLong()
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
protected fun generateUid(id: Long): Long {
|
||||
var h = 1125899906842597L
|
||||
source.name.forEach { c ->
|
||||
h = 31 * h + c.toLong()
|
||||
}
|
||||
h = 31 * h + id
|
||||
return h
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,6 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
loaderContext
|
||||
) {
|
||||
|
||||
protected abstract val defaultDomain: String
|
||||
|
||||
override val sortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.NEWEST,
|
||||
SortOrder.POPULARITY,
|
||||
@@ -27,7 +25,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
sortOrder: SortOrder?,
|
||||
tag: MangaTag?
|
||||
): List<Manga> {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val domain = getDomain()
|
||||
val url = when {
|
||||
!query.isNullOrEmpty() -> {
|
||||
if (offset != 0) {
|
||||
@@ -44,9 +42,9 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
return root.select("div.content_row").mapNotNull { row ->
|
||||
val a = row.selectFirst("div.manga_row1")?.selectFirst("h2")?.selectFirst("a")
|
||||
?: return@mapNotNull null
|
||||
val href = a.attr("href").withDomain(domain)
|
||||
val href = a.relUrl("href")
|
||||
Manga(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
altTitle = a.attr("title"),
|
||||
title = a.text().substringAfterLast('(').substringBeforeLast(')'),
|
||||
@@ -55,7 +53,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
"/mangaka"
|
||||
).firstOrNull()?.text(),
|
||||
coverUrl = row.selectFirst("div.manga_images")?.selectFirst("img")
|
||||
?.attr("src")?.withDomain(domain).orEmpty(),
|
||||
?.absUrl("src").orEmpty(),
|
||||
tags = runCatching {
|
||||
row.selectFirst("div.genre")?.select("a")?.mapToSet {
|
||||
MangaTag(
|
||||
@@ -72,20 +70,18 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val doc = loaderContext.httpGet(manga.url).parseHtml()
|
||||
val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml()
|
||||
val root =
|
||||
doc.body().getElementById("dle-content") ?: throw ParseException("Cannot find root")
|
||||
return manga.copy(
|
||||
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
|
||||
largeCoverUrl = root.getElementById("cover")?.attr("src")?.withDomain(domain),
|
||||
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
|
||||
chapters = root.select("table.table_cha").flatMap { table ->
|
||||
table.select("div.manga2")
|
||||
}.mapNotNull { it.selectFirst("a") }.reversed().mapIndexedNotNull { i, a ->
|
||||
val href = a.attr("href")
|
||||
?.withDomain(domain) ?: return@mapIndexedNotNull null
|
||||
}.map { it.selectFirst("a") }.reversed().mapIndexedNotNull { i, a ->
|
||||
val href = a.relUrl("href")
|
||||
MangaChapter(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
name = a.text().trim(),
|
||||
number = i + 1,
|
||||
url = href,
|
||||
@@ -96,7 +92,8 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val doc = loaderContext.httpGet(chapter.url).parseHtml()
|
||||
val fullUrl = chapter.url.withDomain()
|
||||
val doc = loaderContext.httpGet(fullUrl).parseHtml()
|
||||
val scripts = doc.select("script")
|
||||
for (script in scripts) {
|
||||
val data = script.html()
|
||||
@@ -106,13 +103,17 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
}
|
||||
val json = data.substring(pos).substringAfter('[').substringBefore(';')
|
||||
.substringBeforeLast(']')
|
||||
val domain = getDomain()
|
||||
return json.split(",").mapNotNull {
|
||||
it.trim().removeSurrounding('"', '\'').takeUnless(String::isBlank)
|
||||
it.trim()
|
||||
.removeSurrounding('"', '\'')
|
||||
.toRelativeUrl(domain)
|
||||
.takeUnless(String::isBlank)
|
||||
}.map { url ->
|
||||
MangaPage(
|
||||
id = url.longHashCode(),
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
referer = chapter.url,
|
||||
referer = fullUrl,
|
||||
source = source
|
||||
)
|
||||
}
|
||||
@@ -121,7 +122,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val domain = getDomain()
|
||||
val doc = loaderContext.httpGet("https://$domain/catalog").parseHtml()
|
||||
val root = doc.body().selectFirst("div.main_fon").getElementById("side")
|
||||
.select("ul").last()
|
||||
|
||||
@@ -14,6 +14,8 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
|
||||
override val source = MangaSource.DESUME
|
||||
|
||||
override val defaultDomain = "desu.me"
|
||||
|
||||
override val sortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.UPDATED,
|
||||
SortOrder.POPULARITY,
|
||||
@@ -27,7 +29,7 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
sortOrder: SortOrder?,
|
||||
tag: MangaTag?
|
||||
): List<Manga> {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val domain = getDomain()
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
@@ -51,8 +53,9 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
for (i in 0 until total) {
|
||||
val jo = json.getJSONObject(i)
|
||||
val cover = jo.getJSONObject("image")
|
||||
val id = jo.getLong("id")
|
||||
list += Manga(
|
||||
url = jo.getString("url"),
|
||||
url = "/manga/api/$id",
|
||||
source = MangaSource.DESUME,
|
||||
title = jo.getString("russian"),
|
||||
altTitle = jo.getString("name"),
|
||||
@@ -63,7 +66,7 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
else -> null
|
||||
},
|
||||
rating = jo.getDouble("score").toFloat().coerceIn(0f, 1f),
|
||||
id = ID_MASK + jo.getLong("id"),
|
||||
id = generateUid(id),
|
||||
description = jo.getString("description")
|
||||
)
|
||||
}
|
||||
@@ -71,10 +74,10 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val url = "https://$domain/manga/api/${manga.id - ID_MASK}"
|
||||
val url = manga.url.withDomain()
|
||||
val json = loaderContext.httpGet(url).parseJson().getJSONObject("response")
|
||||
?: throw ParseException("Invalid response")
|
||||
val baseChapterUrl = manga.url + "/chapter/"
|
||||
return manga.copy(
|
||||
tags = json.getJSONArray("genres").mapToSet {
|
||||
MangaTag(
|
||||
@@ -87,9 +90,9 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
chapters = json.getJSONObject("chapters").getJSONArray("list").mapIndexed { i, it ->
|
||||
val chid = it.getLong("id")
|
||||
MangaChapter(
|
||||
id = ID_MASK + chid,
|
||||
id = generateUid(chid),
|
||||
source = manga.source,
|
||||
url = "$url/chapter/$chid",
|
||||
url = "$baseChapterUrl$chid",
|
||||
name = it.optString("title", "${manga.title} #${it.getDouble("ch")}"),
|
||||
number = i + 1
|
||||
)
|
||||
@@ -98,21 +101,22 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val json = loaderContext.httpGet(chapter.url).parseJson().getJSONObject("response")
|
||||
?: throw ParseException("Invalid response")
|
||||
return json.getJSONObject("pages").getJSONArray("list").map {
|
||||
val fullUrl = chapter.url.withDomain()
|
||||
val json = loaderContext.httpGet(fullUrl)
|
||||
.parseJson()
|
||||
.getJSONObject("response") ?: throw ParseException("Invalid response")
|
||||
return json.getJSONObject("pages").getJSONArray("list").map { jo ->
|
||||
MangaPage(
|
||||
id = it.getLong("id"),
|
||||
referer = chapter.url,
|
||||
id = generateUid(jo.getLong("id")),
|
||||
referer = fullUrl,
|
||||
source = chapter.source,
|
||||
url = it.getString("img")
|
||||
url = jo.getString("img")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val doc = loaderContext.httpGet("https://$domain/manga/").parseHtml()
|
||||
val doc = loaderContext.httpGet("https://${getDomain()}/manga/").parseHtml()
|
||||
val root = doc.body().getElementById("animeFilter").selectFirst(".catalog-genres")
|
||||
return root.select("li").mapToSet {
|
||||
MangaTag(
|
||||
@@ -133,10 +137,4 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
SortOrder.NEWEST -> "id"
|
||||
else -> "updated"
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
private const val ID_MASK = 1000
|
||||
private const val DOMAIN = "desu.me"
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,6 @@ import java.util.*
|
||||
abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
RemoteMangaRepository(loaderContext) {
|
||||
|
||||
protected abstract val defaultDomain: String
|
||||
|
||||
override val sortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.UPDATED,
|
||||
SortOrder.POPULARITY,
|
||||
@@ -28,13 +26,13 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
sortOrder: SortOrder?,
|
||||
tag: MangaTag?
|
||||
): List<Manga> {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val domain = getDomain()
|
||||
val doc = when {
|
||||
!query.isNullOrEmpty() -> loaderContext.httpPost(
|
||||
"https://$domain/search",
|
||||
mapOf(
|
||||
"q" to query.urlEncoded(),
|
||||
"offset" to offset.upBy(PAGE_SIZE_SEARCH).toString()
|
||||
"offset" to (offset upBy PAGE_SIZE_SEARCH).toString()
|
||||
)
|
||||
)
|
||||
tag == null -> loaderContext.httpGet(
|
||||
@@ -42,14 +40,14 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
getSortKey(
|
||||
sortOrder
|
||||
)
|
||||
}&offset=${offset.upBy(PAGE_SIZE)}"
|
||||
}&offset=${offset upBy PAGE_SIZE}"
|
||||
)
|
||||
else -> loaderContext.httpGet(
|
||||
"https://$domain/list/genre/${tag.key}?sortType=${
|
||||
getSortKey(
|
||||
sortOrder
|
||||
)
|
||||
}&offset=${offset.upBy(PAGE_SIZE)}"
|
||||
}&offset=${offset upBy PAGE_SIZE}"
|
||||
)
|
||||
}.parseHtml()
|
||||
val root = doc.body().getElementById("mangaBox")
|
||||
@@ -68,9 +66,10 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
val title = descDiv.selectFirst("h3")?.selectFirst("a")?.text()
|
||||
?: return@mapNotNull null
|
||||
val tileInfo = descDiv.selectFirst("div.tile-info")
|
||||
val relUrl = href.toRelativeUrl(baseHost)
|
||||
Manga(
|
||||
id = href.longHashCode(),
|
||||
url = href,
|
||||
id = generateUid(relUrl),
|
||||
url = relUrl,
|
||||
title = title,
|
||||
altTitle = descDiv.selectFirst("h4")?.text(),
|
||||
coverUrl = imgDiv.selectFirst("img.lazy")?.attr("data-original").orEmpty(),
|
||||
@@ -103,8 +102,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val doc = loaderContext.httpGet(manga.url).parseHtml()
|
||||
val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml()
|
||||
val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent")
|
||||
?: throw ParseException("Cannot find root")
|
||||
return manga.copy(
|
||||
@@ -122,11 +120,10 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
)
|
||||
},
|
||||
chapters = root.selectFirst("div.chapters-link")?.selectFirst("table")
|
||||
?.select("a")?.asReversed()?.mapIndexedNotNull { i, a ->
|
||||
val href =
|
||||
a.attr("href")?.withDomain(domain) ?: return@mapIndexedNotNull null
|
||||
?.select("a")?.asReversed()?.mapIndexed { i, a ->
|
||||
val href = a.relUrl("href")
|
||||
MangaChapter(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
name = a.ownText().removePrefix(manga.title).trim(),
|
||||
number = i + 1,
|
||||
url = href,
|
||||
@@ -137,7 +134,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val doc = loaderContext.httpGet(chapter.url + "?mtr=1").parseHtml()
|
||||
val doc = loaderContext.httpGet(chapter.url.withDomain() + "?mtr=1").parseHtml()
|
||||
val scripts = doc.select("script")
|
||||
for (script in scripts) {
|
||||
val data = script.html()
|
||||
@@ -153,7 +150,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
val url = parts[0].value.removeSurrounding('"', '\'') +
|
||||
parts[2].value.removeSurrounding('"', '\'')
|
||||
MangaPage(
|
||||
id = url.longHashCode(),
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
referer = chapter.url,
|
||||
source = source
|
||||
@@ -164,8 +161,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val doc = loaderContext.httpGet("https://$domain/list/genres/sort_name").parseHtml()
|
||||
val doc = loaderContext.httpGet("https://${getDomain()}/list/genres/sort_name").parseHtml()
|
||||
val root = doc.body().getElementById("mangaBox").selectFirst("div.leftContent")
|
||||
.selectFirst("table.table")
|
||||
return root.select("a.element-link").mapToSet { a ->
|
||||
|
||||
@@ -3,10 +3,8 @@ package org.koitharu.kotatsu.core.parser.site
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||
import org.koitharu.kotatsu.core.model.*
|
||||
import org.koitharu.kotatsu.utils.ext.longHashCode
|
||||
import org.koitharu.kotatsu.utils.ext.mapToSet
|
||||
import org.koitharu.kotatsu.utils.ext.parseHtml
|
||||
import org.koitharu.kotatsu.utils.ext.withDomain
|
||||
|
||||
class HenChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(loaderContext) {
|
||||
|
||||
@@ -30,14 +28,13 @@ class HenChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(load
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val doc = loaderContext.httpGet(manga.url).parseHtml()
|
||||
val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml()
|
||||
val root =
|
||||
doc.body().getElementById("dle-content") ?: throw ParseException("Cannot find root")
|
||||
val readLink = manga.url.replace("manga", "online")
|
||||
return manga.copy(
|
||||
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
|
||||
largeCoverUrl = root.getElementById("cover")?.attr("src")?.withDomain(domain),
|
||||
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
|
||||
tags = root.selectFirst("div.sidetags")?.select("li.sidetag")?.mapToSet {
|
||||
val a = it.children().last()
|
||||
MangaTag(
|
||||
@@ -48,7 +45,7 @@ class HenChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(load
|
||||
} ?: manga.tags,
|
||||
chapters = listOf(
|
||||
MangaChapter(
|
||||
id = readLink.longHashCode(),
|
||||
id = generateUid(readLink),
|
||||
url = readLink,
|
||||
source = source,
|
||||
number = 1,
|
||||
|
||||
@@ -17,7 +17,7 @@ import kotlin.collections.ArrayList
|
||||
open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
RemoteMangaRepository(loaderContext) {
|
||||
|
||||
protected open val defaultDomain = "mangalib.me"
|
||||
override val defaultDomain = "mangalib.me"
|
||||
|
||||
override val source = MangaSource.MANGALIB
|
||||
|
||||
@@ -38,11 +38,10 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
if (!query.isNullOrEmpty()) {
|
||||
return search(query)
|
||||
}
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val page = (offset / 60f).toIntUp()
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
append(getDomain())
|
||||
append("/manga-list?dir=")
|
||||
append(getSortKey(sortOrder))
|
||||
append("&page=")
|
||||
@@ -57,11 +56,11 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
val items = root.selectFirst("div.media-cards-grid").select("div.media-card-wrap")
|
||||
return items.mapNotNull { card ->
|
||||
val a = card.selectFirst("a.media-card") ?: return@mapNotNull null
|
||||
val href = a.attr("href").withDomain(domain)
|
||||
val href = a.relUrl("href")
|
||||
Manga(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
title = card.selectFirst("h3").text(),
|
||||
coverUrl = a.attr("data-src").withDomain(domain),
|
||||
coverUrl = a.absUrl("data-src"),
|
||||
altTitle = null,
|
||||
author = null,
|
||||
rating = Manga.NO_RATING,
|
||||
@@ -76,11 +75,12 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
override fun onCreatePreferences() = arraySetOf(SourceSettings.KEY_DOMAIN)
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val doc = loaderContext.httpGet(manga.url + "?section=info").parseHtml()
|
||||
val fullUrl = manga.url.withDomain()
|
||||
val doc = loaderContext.httpGet("$fullUrl?section=info").parseHtml()
|
||||
val root = doc.body().getElementById("main-page") ?: throw ParseException("Root not found")
|
||||
val title = root.selectFirst("div.media-header__wrap")?.children()
|
||||
val info = root.selectFirst("div.media-content")
|
||||
val chaptersDoc = loaderContext.httpGet(manga.url + "?section=chapters").parseHtml()
|
||||
val chaptersDoc = loaderContext.httpGet("$fullUrl?section=chapters").parseHtml()
|
||||
val scripts = chaptersDoc.select("script")
|
||||
var chapters: ArrayList<MangaChapter>? = null
|
||||
scripts@ for (script in scripts) {
|
||||
@@ -109,7 +109,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
}
|
||||
chapters.add(
|
||||
MangaChapter(
|
||||
id = url.longHashCode(),
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
source = source,
|
||||
number = total - i,
|
||||
@@ -144,7 +144,8 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val doc = loaderContext.httpGet(chapter.url).parseHtml()
|
||||
val fullUrl = chapter.url.withDomain()
|
||||
val doc = loaderContext.httpGet(fullUrl).parseHtml()
|
||||
if (doc.location()?.endsWith("/register") == true) {
|
||||
throw AuthRequiredException("/login".inContextOf(doc))
|
||||
}
|
||||
@@ -170,9 +171,9 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
return pages.map { x ->
|
||||
val pageUrl = "$domain/$url${x.getString("u")}"
|
||||
MangaPage(
|
||||
id = pageUrl.longHashCode(),
|
||||
id = generateUid(pageUrl),
|
||||
url = pageUrl,
|
||||
referer = chapter.url,
|
||||
referer = fullUrl,
|
||||
source = source
|
||||
)
|
||||
}
|
||||
@@ -182,8 +183,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val url = "https://$domain/manga-list"
|
||||
val url = "https://${getDomain()}/manga-list"
|
||||
val doc = loaderContext.httpGet(url).parseHtml()
|
||||
val scripts = doc.body().select("script")
|
||||
for (script in scripts) {
|
||||
@@ -215,14 +215,15 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
}
|
||||
|
||||
private suspend fun search(query: String): List<Manga> {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val domain = getDomain()
|
||||
val json = loaderContext.httpGet("https://$domain/search?type=manga&q=$query")
|
||||
.parseJsonArray()
|
||||
return json.map { jo ->
|
||||
val url = "https://$domain/${jo.getString("slug")}"
|
||||
val slug = jo.getString("slug")
|
||||
val url = "https://$domain/$slug"
|
||||
val covers = jo.getJSONObject("covers")
|
||||
Manga(
|
||||
id = url.longHashCode(),
|
||||
id = generateUid(slug),
|
||||
url = url,
|
||||
title = jo.getString("rus_name"),
|
||||
altTitle = jo.getString("name"),
|
||||
|
||||
@@ -15,6 +15,8 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
|
||||
override val source = MangaSource.MANGATOWN
|
||||
|
||||
override val defaultDomain = "www.mangatown.com"
|
||||
|
||||
override val sortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.ALPHABETICAL,
|
||||
SortOrder.RATING,
|
||||
@@ -28,9 +30,6 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
sortOrder: SortOrder?,
|
||||
tag: MangaTag?
|
||||
): List<Manga> {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val ssl = conf.isUseSsl(false)
|
||||
val scheme = if (ssl) "https" else "http"
|
||||
val sortKey = when (sortOrder) {
|
||||
SortOrder.ALPHABETICAL -> "?name.az"
|
||||
SortOrder.RATING -> "?rating.za"
|
||||
@@ -43,29 +42,28 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
if (offset != 0) {
|
||||
return emptyList()
|
||||
}
|
||||
"$scheme://$domain/search?name=${query.urlEncoded()}"
|
||||
"/search?name=${query.urlEncoded()}".withDomain()
|
||||
}
|
||||
tag != null -> "$scheme://$domain/directory/${tag.key}/$page.htm$sortKey"
|
||||
else -> "$scheme://$domain/directory/$page.htm$sortKey"
|
||||
tag != null -> "/directory/${tag.key}/$page.htm$sortKey".withDomain()
|
||||
else -> "/directory/$page.htm$sortKey".withDomain()
|
||||
}
|
||||
val doc = loaderContext.httpGet(url).parseHtml()
|
||||
val root = doc.body().selectFirst("ul.manga_pic_list")
|
||||
?: throw ParseException("Root not found")
|
||||
return root.select("li").mapNotNull { li ->
|
||||
val a = li.selectFirst("a.manga_cover")
|
||||
val href = a.attr("href").withDomain(domain, ssl)
|
||||
val href = a.relUrl("href")
|
||||
val views = li.select("p.view")
|
||||
val status = views.findOwnText { x -> x.startsWith("Status:") }
|
||||
?.substringAfter(':')?.trim()?.toLowerCase(Locale.ROOT)
|
||||
Manga(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
title = a.attr("title"),
|
||||
coverUrl = a.selectFirst("img").attr("src"),
|
||||
coverUrl = a.selectFirst("img").absUrl("src"),
|
||||
source = MangaSource.MANGATOWN,
|
||||
altTitle = null,
|
||||
rating = li.selectFirst("p.score")?.selectFirst("b")
|
||||
?.ownText()?.toFloatOrNull()?.div(5f) ?: Manga.NO_RATING,
|
||||
largeCoverUrl = null,
|
||||
author = views.findText { x -> x.startsWith("Author:") }?.substringAfter(':')
|
||||
?.trim(),
|
||||
state = when (status) {
|
||||
@@ -86,9 +84,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val ssl = conf.isUseSsl(false)
|
||||
val doc = loaderContext.httpGet(manga.url).parseHtml()
|
||||
val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml()
|
||||
val root = doc.body().selectFirst("section.main")
|
||||
?.selectFirst("div.article_content") ?: throw ParseException("Cannot find root")
|
||||
val info = root.selectFirst("div.detail_info").selectFirst("ul")
|
||||
@@ -106,11 +102,11 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
}.orEmpty(),
|
||||
description = info.getElementById("show")?.ownText(),
|
||||
chapters = chaptersList?.mapIndexedNotNull { i, li ->
|
||||
val href = li.selectFirst("a").attr("href").withDomain(domain, ssl)
|
||||
val href = li.selectFirst("a").relUrl("href")
|
||||
val name = li.select("span").filter { it.className().isEmpty() }
|
||||
.joinToString(" - ") { it.text() }.trim()
|
||||
MangaChapter(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
source = MangaSource.MANGATOWN,
|
||||
number = i + 1,
|
||||
@@ -121,35 +117,31 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val ssl = conf.isUseSsl(false)
|
||||
val doc = loaderContext.httpGet(chapter.url).parseHtml()
|
||||
val fullUrl = chapter.url.withDomain()
|
||||
val doc = loaderContext.httpGet(fullUrl).parseHtml()
|
||||
val root = doc.body().selectFirst("div.page_select")
|
||||
?: throw ParseException("Cannot find root")
|
||||
return root.selectFirst("select").select("option").mapNotNull {
|
||||
val href = it.attr("value").withDomain(domain, ssl)
|
||||
val href = it.relUrl("value")
|
||||
if (href.endsWith("featured.html")) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
MangaPage(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
referer = chapter.url,
|
||||
referer = fullUrl,
|
||||
source = MangaSource.MANGATOWN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPageUrl(page: MangaPage): String {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val ssl = conf.isUseSsl(false)
|
||||
val doc = loaderContext.httpGet(page.url).parseHtml()
|
||||
return doc.getElementById("image").attr("src").withDomain(domain, ssl)
|
||||
val doc = loaderContext.httpGet(page.url.withDomain()).parseHtml()
|
||||
return doc.getElementById("image").absUrl("src")
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val doc = loaderContext.httpGet("http://$domain/directory/").parseHtml()
|
||||
val doc = loaderContext.httpGet("/directory/".withDomain()).parseHtml()
|
||||
val root = doc.body().selectFirst("aside.right")
|
||||
.getElementsContainingOwnText("Genres")
|
||||
.first()
|
||||
@@ -180,6 +172,5 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
|
||||
@Language("RegExp")
|
||||
val TAG_REGEX = Regex("[^\\-]+-[^\\-]+-[^\\-]+-[^\\-]+-[^\\-]+-[^\\-]+")
|
||||
const val DOMAIN = "www.mangatown.com"
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ class MangareadRepository(
|
||||
|
||||
override val source = MangaSource.MANGAREAD
|
||||
|
||||
override val defaultDomain = "www.mangaread.org"
|
||||
|
||||
override val sortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.UPDATED,
|
||||
SortOrder.POPULARITY
|
||||
@@ -29,7 +31,6 @@ class MangareadRepository(
|
||||
if (offset % PAGE_SIZE != 0) {
|
||||
return emptyList()
|
||||
}
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val payload = createRequestTemplate()
|
||||
payload["page"] = (offset / PAGE_SIZE).toString()
|
||||
payload["vars[meta_key]"] = when (sortOrder) {
|
||||
@@ -40,14 +41,14 @@ class MangareadRepository(
|
||||
payload["vars[wp-manga-genre]"] = tag?.key.orEmpty()
|
||||
payload["vars[s]"] = query.orEmpty()
|
||||
val doc = loaderContext.httpPost(
|
||||
"https://${domain}/wp-admin/admin-ajax.php",
|
||||
"https://${getDomain()}/wp-admin/admin-ajax.php",
|
||||
payload
|
||||
).parseHtml()
|
||||
return doc.select("div.row.c-tabs-item__content").map { div ->
|
||||
val href = div.selectFirst("a").absUrl("href")
|
||||
val href = div.selectFirst("a").relUrl("href")
|
||||
val summary = div.selectFirst(".tab-summary")
|
||||
Manga(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
coverUrl = div.selectFirst("img").attr("data-srcset")
|
||||
.split(',').firstOrNull()?.substringBeforeLast(' ').orEmpty(),
|
||||
@@ -74,8 +75,7 @@ class MangareadRepository(
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val doc = loaderContext.httpGet("https://$domain/manga/").parseHtml()
|
||||
val doc = loaderContext.httpGet("https://${getDomain()}/manga/").parseHtml()
|
||||
val root = doc.body().selectFirst("header")
|
||||
.selectFirst("ul.second-menu")
|
||||
return root.select("li").mapNotNullToSet { li ->
|
||||
@@ -94,8 +94,8 @@ class MangareadRepository(
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val doc = loaderContext.httpGet(manga.url).parseHtml()
|
||||
val fullUrl = manga.url.withDomain()
|
||||
val doc = loaderContext.httpGet(fullUrl).parseHtml()
|
||||
val root = doc.body().selectFirst("div.profile-manga")
|
||||
?.selectFirst("div.summary_content")
|
||||
?.selectFirst("div.post-content")
|
||||
@@ -107,7 +107,7 @@ class MangareadRepository(
|
||||
?.attr("data-postid")?.toLongOrNull()
|
||||
?: throw ParseException("Cannot obtain manga id")
|
||||
val doc2 = loaderContext.httpPost(
|
||||
"https://${domain}/wp-admin/admin-ajax.php",
|
||||
"https://${getDomain()}/wp-admin/admin-ajax.php",
|
||||
mapOf(
|
||||
"action" to "manga_get_chapters",
|
||||
"manga" to mangaId.toString()
|
||||
@@ -129,9 +129,9 @@ class MangareadRepository(
|
||||
?.joinToString { it.html() },
|
||||
chapters = doc2.select("li").asReversed().mapIndexed { i, li ->
|
||||
val a = li.selectFirst("a")
|
||||
val href = a.absUrl("href")
|
||||
val href = a.relUrl("href")
|
||||
MangaChapter(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
name = a.ownText(),
|
||||
number = i + 1,
|
||||
url = href,
|
||||
@@ -142,17 +142,18 @@ class MangareadRepository(
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val doc = loaderContext.httpGet(chapter.url).parseHtml()
|
||||
val fullUrl = chapter.url.withDomain()
|
||||
val doc = loaderContext.httpGet(fullUrl).parseHtml()
|
||||
val root = doc.body().selectFirst("div.main-col-inner")
|
||||
?.selectFirst("div.reading-content")
|
||||
?: throw ParseException("Root not found")
|
||||
return root.select("div.page-break").map { div ->
|
||||
val img = div.selectFirst("img")
|
||||
val url = img.absUrl("data-src")
|
||||
val url = img.relUrl("data-src")
|
||||
MangaPage(
|
||||
id = url.longHashCode(),
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
referer = chapter.url,
|
||||
referer = fullUrl,
|
||||
source = MangaSource.MANGAREAD
|
||||
)
|
||||
}
|
||||
@@ -163,7 +164,6 @@ class MangareadRepository(
|
||||
private companion object {
|
||||
|
||||
private const val PAGE_SIZE = 12
|
||||
private const val DOMAIN = "www.mangaread.org"
|
||||
|
||||
private fun createRequestTemplate() =
|
||||
"action=madara_load_more&page=1&template=madara-core%2Fcontent%2Fcontent-search&vars%5Bs%5D=&vars%5Borderby%5D=meta_value_num&vars%5Bpaged%5D=1&vars%5Btemplate%5D=search&vars%5Bmeta_query%5D%5B0%5D%5Brelation%5D=AND&vars%5Bmeta_query%5D%5Brelation%5D=OR&vars%5Bpost_type%5D=wp-manga&vars%5Bpost_status%5D=publish&vars%5Bmeta_key%5D=_latest_update&vars%5Border%5D=desc&vars%5Bmanga_archives_item_layout%5D=default"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.koitharu.kotatsu.core.parser.site
|
||||
|
||||
import androidx.collection.arraySetOf
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
@@ -18,6 +17,8 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
|
||||
override val source = MangaSource.REMANGA
|
||||
|
||||
override val defaultDomain = "remanga.org"
|
||||
|
||||
override val sortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.POPULARITY,
|
||||
SortOrder.RATING,
|
||||
@@ -32,7 +33,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
sortOrder: SortOrder?,
|
||||
tag: MangaTag?
|
||||
): List<Manga> {
|
||||
val domain = conf.getDomain(DEFAULT_DOMAIN)
|
||||
val domain = getDomain()
|
||||
val urlBuilder = StringBuilder()
|
||||
.append("https://api.")
|
||||
.append(domain)
|
||||
@@ -40,20 +41,21 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
urlBuilder.append("/api/search/?query=")
|
||||
.append(query.urlEncoded())
|
||||
} else {
|
||||
urlBuilder.append("/api/search/catalog/?page=")
|
||||
.append("&ordering=")
|
||||
urlBuilder.append("/api/search/catalog/?ordering=")
|
||||
.append(getSortKey(sortOrder))
|
||||
if (tag != null) {
|
||||
urlBuilder.append("&genres=" + tag.key)
|
||||
}
|
||||
}
|
||||
urlBuilder.append((offset / PAGE_SIZE) + 1)
|
||||
urlBuilder
|
||||
.append("&page=")
|
||||
.append((offset / PAGE_SIZE) + 1)
|
||||
.append("&count=")
|
||||
.append(PAGE_SIZE)
|
||||
val content = loaderContext.httpGet(urlBuilder.toString()).parseJson()
|
||||
.getJSONArray("content")
|
||||
return content.map { jo ->
|
||||
val url = "https://$domain/manga/${jo.getString("dir")}"
|
||||
val url = "/manga/${jo.getString("dir")}"
|
||||
val img = jo.getJSONObject("img")
|
||||
Manga(
|
||||
id = generateUid(url),
|
||||
@@ -77,8 +79,9 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val domain = conf.getDomain(DEFAULT_DOMAIN)
|
||||
val slug = manga.url.toHttpUrl().pathSegments.last()
|
||||
val domain = getDomain()
|
||||
val slug = manga.url.find(LAST_URL_PATH_REGEX)
|
||||
?: throw ParseException("Cannot obtain slug from ${manga.url}")
|
||||
val data = loaderContext.httpGet(
|
||||
url = "https://api.$domain/api/titles/$slug/"
|
||||
).parseJson()
|
||||
@@ -110,7 +113,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
val id = jo.getLong("id")
|
||||
val name = jo.getString("name")
|
||||
MangaChapter(
|
||||
id = generateUid(id.toInt()),
|
||||
id = generateUid(id),
|
||||
url = "https://api.$domain/api/titles/chapters/$id/",
|
||||
number = chapters.length() - i,
|
||||
name = buildString {
|
||||
@@ -128,8 +131,8 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val referer = "https://${conf.getDomain(DEFAULT_DOMAIN)}/"
|
||||
val content = loaderContext.httpGet(chapter.url).parseJson()
|
||||
val referer = "https://${getDomain()}/"
|
||||
val content = loaderContext.httpGet(chapter.url.withDomain()).parseJson()
|
||||
.getJSONObject("content").getJSONArray("pages")
|
||||
val pages = ArrayList<MangaPage>(content.length())
|
||||
for (i in 0 until content.length()) {
|
||||
@@ -143,7 +146,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val domain = conf.getDomain(DEFAULT_DOMAIN)
|
||||
val domain = getDomain()
|
||||
val content = loaderContext.httpGet("https://api.$domain/api/forms/titles/?get=genres")
|
||||
.parseJson().getJSONObject("content").getJSONArray("genres")
|
||||
return content.mapToSet { jo ->
|
||||
@@ -166,7 +169,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
}
|
||||
|
||||
private fun parsePage(jo: JSONObject, referer: String) = MangaPage(
|
||||
id = generateUid(jo.getLong("id").toInt()),
|
||||
id = generateUid(jo.getLong("id")),
|
||||
url = jo.getString("link"),
|
||||
referer = referer,
|
||||
source = source
|
||||
@@ -175,9 +178,10 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
private companion object {
|
||||
|
||||
const val PAGE_SIZE = 30
|
||||
const val DEFAULT_DOMAIN = "remanga.org"
|
||||
|
||||
const val STATUS_ONGOING = 1
|
||||
const val STATUS_FINISHED = 0
|
||||
|
||||
val LAST_URL_PATH_REGEX = Regex("/[^/]+/?$")
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,8 @@ import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaChapter
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.utils.ext.longHashCode
|
||||
import org.koitharu.kotatsu.utils.ext.parseHtml
|
||||
import org.koitharu.kotatsu.utils.ext.withDomain
|
||||
import org.koitharu.kotatsu.utils.ext.relUrl
|
||||
|
||||
class YaoiChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(loaderContext) {
|
||||
|
||||
@@ -15,20 +14,18 @@ class YaoiChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(loa
|
||||
override val defaultDomain = "yaoi-chan.me"
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val domain = conf.getDomain(defaultDomain)
|
||||
val doc = loaderContext.httpGet(manga.url).parseHtml()
|
||||
val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml()
|
||||
val root =
|
||||
doc.body().getElementById("dle-content") ?: throw ParseException("Cannot find root")
|
||||
return manga.copy(
|
||||
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
|
||||
largeCoverUrl = root.getElementById("cover")?.attr("src")?.withDomain(domain),
|
||||
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
|
||||
chapters = root.select("table.table_cha").flatMap { table ->
|
||||
table.select("div.manga")
|
||||
}.mapNotNull { it.selectFirst("a") }.reversed().mapIndexedNotNull { i, a ->
|
||||
val href = a.attr("href")
|
||||
?.withDomain(domain) ?: return@mapIndexedNotNull null
|
||||
}.mapNotNull { it.selectFirst("a") }.reversed().mapIndexed { i, a ->
|
||||
val href = a.relUrl("href")
|
||||
MangaChapter(
|
||||
id = href.longHashCode(),
|
||||
id = generateUid(href),
|
||||
name = a.text().trim(),
|
||||
number = i + 1,
|
||||
url = href,
|
||||
|
||||
@@ -217,7 +217,7 @@ class ReaderViewModel(
|
||||
|
||||
private suspend fun loadChapter(chapterId: Long): List<ReaderPage> {
|
||||
val manga = checkNotNull(mangaData.value) { "Manga is null" }
|
||||
val chapter = checkNotNull(chapters.get(chapterId)) { "Chapter $chapterId not found" }
|
||||
val chapter = checkNotNull(chapters[chapterId]) { "Requested chapter not found" }
|
||||
val repo = manga.source.repository
|
||||
return repo.getPages(chapter).mapIndexed { index, page ->
|
||||
ReaderPage.from(page, index, chapterId)
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.json.JSONObject
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.internal.StringUtil
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import org.jsoup.nodes.Node
|
||||
import org.jsoup.select.Elements
|
||||
|
||||
@@ -69,4 +70,25 @@ fun String.inContextOf(node: Node): String {
|
||||
} else {
|
||||
StringUtil.resolve(node.baseUri(), this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun String.toRelativeUrl(domain: String): String {
|
||||
if (isEmpty() || startsWith("/")) {
|
||||
return this
|
||||
}
|
||||
return replace(Regex("^[^/]{2,6}://${Regex.escape(domain)}+/", RegexOption.IGNORE_CASE), "/")
|
||||
}
|
||||
|
||||
fun Element.relUrl(attributeKey: String): String {
|
||||
val attr = attr(attributeKey)
|
||||
if (attr.isEmpty()) {
|
||||
return ""
|
||||
}
|
||||
if (attr.startsWith("/")) {
|
||||
return attr
|
||||
}
|
||||
val baseUrl = REGEX_URL_BASE.find(baseUri())?.value ?: return attr
|
||||
return attr.removePrefix(baseUrl.dropLast(1))
|
||||
}
|
||||
|
||||
private val REGEX_URL_BASE = Regex("^[^/]{2,6}://[^/]+/", RegexOption.IGNORE_CASE)
|
||||
@@ -33,7 +33,7 @@ fun Float.toIntUp(): Int {
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.upBy(step: Int): Int {
|
||||
infix fun Int.upBy(step: Int): Int {
|
||||
val mod = this % step
|
||||
return if (mod == this) {
|
||||
this
|
||||
|
||||
@@ -16,28 +16,6 @@ fun String.longHashCode(): Long {
|
||||
return h
|
||||
}
|
||||
|
||||
@Deprecated("Use String.inContextOf")
|
||||
fun String.withDomain(domain: String, ssl: Boolean = true) = when {
|
||||
this.startsWith("//") -> buildString {
|
||||
append("http")
|
||||
if (ssl) {
|
||||
append('s')
|
||||
}
|
||||
append(":")
|
||||
append(this@withDomain)
|
||||
}
|
||||
this.startsWith("/") -> buildString {
|
||||
append("http")
|
||||
if (ssl) {
|
||||
append('s')
|
||||
}
|
||||
append("://")
|
||||
append(domain)
|
||||
append(this@withDomain)
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
|
||||
fun String.removeSurrounding(vararg chars: Char): String {
|
||||
if (length == 0) {
|
||||
return this
|
||||
@@ -124,4 +102,6 @@ fun String.substringBetween(from: String, to: String, fallbackValue: String): St
|
||||
} else {
|
||||
substring(fromIndex + from.length, toIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun String.find(regex: Regex) = regex.find(this)?.value
|
||||
Reference in New Issue
Block a user