|
|
|
@ -1,32 +1,33 @@
|
|
|
|
package org.koitharu.kotatsu.parsers.site.all
|
|
|
|
package org.koitharu.kotatsu.parsers.site.all
|
|
|
|
|
|
|
|
|
|
|
|
import org.json.JSONArray
|
|
|
|
|
|
|
|
import org.json.JSONObject
|
|
|
|
|
|
|
|
import org.jsoup.HttpStatusException
|
|
|
|
|
|
|
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
|
|
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
|
|
|
|
|
|
|
import org.json.JSONObject
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.Broken
|
|
|
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
|
|
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
|
|
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
|
|
|
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
|
|
|
import org.koitharu.kotatsu.parsers.model.*
|
|
|
|
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
|
|
|
import org.koitharu.kotatsu.parsers.core.LegacyPagedMangaParser
|
|
|
|
import org.koitharu.kotatsu.parsers.core.LegacyPagedMangaParser
|
|
|
|
import org.koitharu.kotatsu.parsers.exception.ParseException
|
|
|
|
import org.koitharu.kotatsu.parsers.exception.ParseException
|
|
|
|
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.*
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.*
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.getIntOrDefault
|
|
|
|
import java.net.HttpURLConnection
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.getLongOrDefault
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.mapJSONNotNullToSet
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.suspendlazy.getOrDefault
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.suspendlazy.suspendLazy
|
|
|
|
import java.text.SimpleDateFormat
|
|
|
|
import java.text.SimpleDateFormat
|
|
|
|
import java.util.*
|
|
|
|
import java.util.*
|
|
|
|
import org.koitharu.kotatsu.parsers.Broken
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Broken("Need fix getPages + fetchTags + fetchAuthors")
|
|
|
|
@Broken("Need fix getPages")
|
|
|
|
@MangaSourceParser("KOHARU", "Schale.network", type = ContentType.HENTAI)
|
|
|
|
@MangaSourceParser("KOHARU", "Schale Network", type = ContentType.HENTAI)
|
|
|
|
internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
LegacyPagedMangaParser(context, MangaParserSource.KOHARU, 24) {
|
|
|
|
LegacyPagedMangaParser(context, MangaParserSource.KOHARU, 24) {
|
|
|
|
|
|
|
|
|
|
|
|
override val configKeyDomain = ConfigKey.Domain("niyaniya.moe")
|
|
|
|
override val configKeyDomain = ConfigKey.Domain("niyaniya.moe")
|
|
|
|
private val apiSuffix = "api.schale.network"
|
|
|
|
private val apiSuffix = "api.schale.network"
|
|
|
|
|
|
|
|
|
|
|
|
private var authorMap: Map<String, String>? = null
|
|
|
|
private val authorsIds = suspendLazy { fetchAuthorsIds() }
|
|
|
|
|
|
|
|
|
|
|
|
private val preferredImageResolutionKey = ConfigKey.PreferredImageServer(
|
|
|
|
private val preferredImageResolutionKey = ConfigKey.PreferredImageServer(
|
|
|
|
presetValues = mapOf(
|
|
|
|
presetValues = mapOf(
|
|
|
|
@ -57,7 +58,7 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
SortOrder.POPULARITY_WEEK,
|
|
|
|
SortOrder.POPULARITY_WEEK,
|
|
|
|
SortOrder.ALPHABETICAL,
|
|
|
|
SortOrder.ALPHABETICAL,
|
|
|
|
SortOrder.ALPHABETICAL_DESC,
|
|
|
|
SortOrder.ALPHABETICAL_DESC,
|
|
|
|
SortOrder.RATING
|
|
|
|
SortOrder.RATING,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
override val filterCapabilities: MangaListFilterCapabilities
|
|
|
|
override val filterCapabilities: MangaListFilterCapabilities
|
|
|
|
@ -66,11 +67,11 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
isSearchSupported = true,
|
|
|
|
isSearchSupported = true,
|
|
|
|
isAuthorSearchSupported = true,
|
|
|
|
isAuthorSearchSupported = true,
|
|
|
|
isSearchWithFiltersSupported = true,
|
|
|
|
isSearchWithFiltersSupported = true,
|
|
|
|
isTagsExclusionSupported = true
|
|
|
|
isTagsExclusionSupported = true,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
override suspend fun getFilterOptions() = MangaListFilterOptions(
|
|
|
|
override suspend fun getFilterOptions() = MangaListFilterOptions(
|
|
|
|
availableTags = fetchTags()
|
|
|
|
availableTags = fetchTags(namespace = 0),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
|
|
|
|
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
|
|
|
|
@ -104,9 +105,8 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!filter.author.isNullOrEmpty()) {
|
|
|
|
if (!filter.author.isNullOrEmpty()) {
|
|
|
|
val authors = fetchAuthors()
|
|
|
|
val authors = authorsIds.getOrDefault(emptyMap())
|
|
|
|
val authorId = authors[filter.author] ?:
|
|
|
|
val authorId = authors[filter.author.lowercase()]
|
|
|
|
authors.entries.find { it.key.equals(filter.author, ignoreCase = true) }?.value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (authorId != null) {
|
|
|
|
if (authorId != null) {
|
|
|
|
includedTags.add(authorId)
|
|
|
|
includedTags.add(authorId)
|
|
|
|
@ -141,8 +141,11 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val json = webClient.httpGet(url).parseJson()
|
|
|
|
val json = webClient.httpGet(url).parseJson()
|
|
|
|
if (json.has("error") || json.has("message")) {
|
|
|
|
json.getStringOrNull("error")?.let {
|
|
|
|
return emptyList()
|
|
|
|
throw ParseException(it, url)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
json.getStringOrNull("message")?.let {
|
|
|
|
|
|
|
|
throw ParseException(it, url)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return parseMangaList(json)
|
|
|
|
return parseMangaList(json)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -153,13 +156,13 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
|
|
|
|
|
|
|
|
for (i in 0 until entries.length()) {
|
|
|
|
for (i in 0 until entries.length()) {
|
|
|
|
val entry = entries.getJSONObject(i)
|
|
|
|
val entry = entries.getJSONObject(i)
|
|
|
|
val id = entry.getInt("id")
|
|
|
|
val id = entry.getLong("id")
|
|
|
|
val key = entry.getString("key")
|
|
|
|
val key = entry.getString("key")
|
|
|
|
val url = "$id/$key"
|
|
|
|
val url = "$id/$key"
|
|
|
|
|
|
|
|
|
|
|
|
results.add(
|
|
|
|
results.add(
|
|
|
|
Manga(
|
|
|
|
Manga(
|
|
|
|
id = generateUid(url),
|
|
|
|
id = generateUid(id),
|
|
|
|
url = url,
|
|
|
|
url = url,
|
|
|
|
publicUrl = "https://$domain/g/$url",
|
|
|
|
publicUrl = "https://$domain/g/$url",
|
|
|
|
title = entry.getString("title"),
|
|
|
|
title = entry.getString("title"),
|
|
|
|
@ -167,11 +170,11 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
authors = emptySet(),
|
|
|
|
authors = emptySet(),
|
|
|
|
tags = emptySet(),
|
|
|
|
tags = emptySet(),
|
|
|
|
rating = RATING_UNKNOWN,
|
|
|
|
rating = RATING_UNKNOWN,
|
|
|
|
state = MangaState.FINISHED,
|
|
|
|
state = null,
|
|
|
|
coverUrl = entry.getJSONObject("thumbnail").getString("path"),
|
|
|
|
coverUrl = entry.getJSONObject("thumbnail").getString("path"),
|
|
|
|
contentRating = ContentRating.ADULT,
|
|
|
|
contentRating = ContentRating.ADULT,
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -180,7 +183,7 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
|
|
|
|
|
|
|
|
private fun parseMangaDetail(json: JSONObject): Manga {
|
|
|
|
private fun parseMangaDetail(json: JSONObject): Manga {
|
|
|
|
val data = json.getJSONObject("data")
|
|
|
|
val data = json.getJSONObject("data")
|
|
|
|
val id = data.getInt("id")
|
|
|
|
val id = data.getLong("id")
|
|
|
|
val key = data.getString("key")
|
|
|
|
val key = data.getString("key")
|
|
|
|
val url = "$id/$key"
|
|
|
|
val url = "$id/$key"
|
|
|
|
|
|
|
|
|
|
|
|
@ -197,7 +200,7 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Manga(
|
|
|
|
return Manga(
|
|
|
|
id = generateUid(url),
|
|
|
|
id = generateUid(id),
|
|
|
|
url = url,
|
|
|
|
url = url,
|
|
|
|
publicUrl = "https://$domain/g/$url",
|
|
|
|
publicUrl = "https://$domain/g/$url",
|
|
|
|
title = data.getString("title"),
|
|
|
|
title = data.getString("title"),
|
|
|
|
@ -205,7 +208,7 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
authors = setOfNotNull(author),
|
|
|
|
authors = setOfNotNull(author),
|
|
|
|
tags = emptySet(),
|
|
|
|
tags = emptySet(),
|
|
|
|
rating = RATING_UNKNOWN,
|
|
|
|
rating = RATING_UNKNOWN,
|
|
|
|
state = MangaState.FINISHED,
|
|
|
|
state = null,
|
|
|
|
coverUrl = data.getJSONObject("thumbnails").getJSONObject("main").getString("path"),
|
|
|
|
coverUrl = data.getJSONObject("thumbnails").getJSONObject("main").getString("path"),
|
|
|
|
contentRating = ContentRating.ADULT,
|
|
|
|
contentRating = ContentRating.ADULT,
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
@ -216,7 +219,7 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
val url = manga.url
|
|
|
|
val url = manga.url
|
|
|
|
val response = webClient.httpGet("https://$apiSuffix/books/detail/$url").parseJson()
|
|
|
|
val response = webClient.httpGet("https://$apiSuffix/books/detail/$url").parseJson()
|
|
|
|
|
|
|
|
|
|
|
|
val id = response.getInt("id")
|
|
|
|
val id = response.getLong("id")
|
|
|
|
val key = response.getString("key")
|
|
|
|
val key = response.getString("key")
|
|
|
|
val mangaUrl = "$id/$key"
|
|
|
|
val mangaUrl = "$id/$key"
|
|
|
|
|
|
|
|
|
|
|
|
@ -235,13 +238,14 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
1 -> {
|
|
|
|
1 -> {
|
|
|
|
author = tagName
|
|
|
|
author = tagName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
0, 3, 8, 9, 10, 12 -> {
|
|
|
|
0, 3, 8, 9, 10, 12 -> {
|
|
|
|
tagsList.add(
|
|
|
|
tagsList.add(
|
|
|
|
MangaTag(
|
|
|
|
MangaTag(
|
|
|
|
key = tagName,
|
|
|
|
key = tagName,
|
|
|
|
title = tagName.toTitleCase(sourceLocale),
|
|
|
|
title = tagName.toTitleCase(sourceLocale),
|
|
|
|
source = source
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -251,8 +255,8 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
MangaTag(
|
|
|
|
MangaTag(
|
|
|
|
key = tagName,
|
|
|
|
key = tagName,
|
|
|
|
title = tagName.toTitleCase(sourceLocale),
|
|
|
|
title = tagName.toTitleCase(sourceLocale),
|
|
|
|
source = source
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -261,12 +265,12 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
val description = buildString {
|
|
|
|
val description = buildString {
|
|
|
|
val created = response.getLongOrDefault("created_at", 0L)
|
|
|
|
val created = response.getLongOrDefault("created_at", 0L)
|
|
|
|
if (created > 0) {
|
|
|
|
if (created > 0) {
|
|
|
|
append("Posted: ").append(SimpleDateFormat("yyyy-MM-dd", Locale.US).format(created)).append("\n")
|
|
|
|
append("<b>Posted:</b> ").append(SimpleDateFormat("yyyy-MM-dd", Locale.US).format(created)).append("\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val thumbnails = response.getJSONObject("thumbnails")
|
|
|
|
val thumbnails = response.getJSONObject("thumbnails")
|
|
|
|
val pageCount = thumbnails.optJSONArray("entries")?.length() ?: 0
|
|
|
|
val pageCount = thumbnails.optJSONArray("entries")?.length() ?: 0
|
|
|
|
append("Pages: ").append(pageCount)
|
|
|
|
append("<b>Pages:</b> ").append(pageCount)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val thumbnails = response.getJSONObject("thumbnails")
|
|
|
|
val thumbnails = response.getJSONObject("thumbnails")
|
|
|
|
@ -275,7 +279,7 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
val coverUrl = base + mainPath
|
|
|
|
val coverUrl = base + mainPath
|
|
|
|
|
|
|
|
|
|
|
|
return Manga(
|
|
|
|
return Manga(
|
|
|
|
id = generateUid(mangaUrl),
|
|
|
|
id = generateUid(id),
|
|
|
|
url = mangaUrl,
|
|
|
|
url = mangaUrl,
|
|
|
|
publicUrl = "https://$domain/g/$mangaUrl",
|
|
|
|
publicUrl = "https://$domain/g/$mangaUrl",
|
|
|
|
title = response.getString("title"),
|
|
|
|
title = response.getString("title"),
|
|
|
|
@ -291,7 +295,7 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
chapters = listOf(
|
|
|
|
chapters = listOf(
|
|
|
|
MangaChapter(
|
|
|
|
MangaChapter(
|
|
|
|
id = generateUid("$mangaUrl/chapter"),
|
|
|
|
id = generateUid("$mangaUrl/chapter"),
|
|
|
|
title = "Oneshot",
|
|
|
|
title = null,
|
|
|
|
number = 1f,
|
|
|
|
number = 1f,
|
|
|
|
url = mangaUrl,
|
|
|
|
url = mangaUrl,
|
|
|
|
scanlator = null,
|
|
|
|
scanlator = null,
|
|
|
|
@ -299,18 +303,19 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
branch = null,
|
|
|
|
branch = null,
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
volume = 0,
|
|
|
|
volume = 0,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Volatile
|
|
|
|
private var cachedToken: String? = null
|
|
|
|
private var cachedToken: String? = null
|
|
|
|
|
|
|
|
|
|
|
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
|
|
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
|
|
|
val mangaUrl = chapter.url
|
|
|
|
val mangaUrl = chapter.url
|
|
|
|
val parts = mangaUrl.split("/")
|
|
|
|
val parts = mangaUrl.split('/')
|
|
|
|
if (parts.size < 2) {
|
|
|
|
if (parts.size < 2) {
|
|
|
|
throw Exception("Invalid URL")
|
|
|
|
throw ParseException("Invalid URL", mangaUrl)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val id = parts[0]
|
|
|
|
val id = parts[0]
|
|
|
|
@ -318,43 +323,40 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
|
|
|
|
|
|
|
|
val detailResponse = webClient.httpGet("https://$apiSuffix/books/detail/$id/$key").parseJson()
|
|
|
|
val detailResponse = webClient.httpGet("https://$apiSuffix/books/detail/$id/$key").parseJson()
|
|
|
|
|
|
|
|
|
|
|
|
var currentToken = detailResponse.optString("crt", cachedToken ?: "")
|
|
|
|
var currentToken = detailResponse.getStringOrNull("crt") ?: cachedToken
|
|
|
|
|
|
|
|
|
|
|
|
if (currentToken.isEmpty()) {
|
|
|
|
if (currentToken.isNullOrEmpty()) {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
val noTokenResponse = webClient.httpPost(
|
|
|
|
val noTokenResponse = webClient.httpPost(
|
|
|
|
url = "https://$apiSuffix/books/detail/$id/$key".toHttpUrl(),
|
|
|
|
url = "https://$apiSuffix/books/detail/$id/$key".toHttpUrl(),
|
|
|
|
form = emptyMap(),
|
|
|
|
form = emptyMap(),
|
|
|
|
extraHeaders = getRequestHeaders()
|
|
|
|
extraHeaders = getRequestHeaders(),
|
|
|
|
).parseJson()
|
|
|
|
).parseJson()
|
|
|
|
|
|
|
|
|
|
|
|
currentToken = noTokenResponse.optString("crt", "")
|
|
|
|
currentToken = noTokenResponse.getStringOrNull("crt")
|
|
|
|
if (currentToken.isNotEmpty()) {
|
|
|
|
if (!currentToken.isNullOrEmpty()) {
|
|
|
|
cachedToken = currentToken
|
|
|
|
cachedToken = currentToken
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e: Exception) {
|
|
|
|
} catch (e: Exception) {
|
|
|
|
throw Exception("Cant get auth token!")
|
|
|
|
throw IllegalStateException("Cant get auth token", e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (currentToken.isEmpty()) {
|
|
|
|
if (currentToken.isNullOrEmpty()) {
|
|
|
|
throw Exception("Cant get auth token!")
|
|
|
|
throw IllegalStateException("Cant get auth token")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val dataResponse = try {
|
|
|
|
val dataUrl = "https://$apiSuffix/books/detail/$id/$key?crt=$currentToken"
|
|
|
|
webClient.httpPost(
|
|
|
|
val dataResponse = webClient.httpPost(
|
|
|
|
url = "https://$apiSuffix/books/detail/$id/$key?crt=$currentToken".toHttpUrl(),
|
|
|
|
url = dataUrl.toHttpUrl(),
|
|
|
|
form = emptyMap(),
|
|
|
|
form = emptyMap(),
|
|
|
|
extraHeaders = getRequestHeaders()
|
|
|
|
extraHeaders = getRequestHeaders(),
|
|
|
|
).parseJson()
|
|
|
|
).parseJson()
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
|
|
|
throw Exception("Cant send POST request: ${e.message}. Token may be expired.")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val data = try {
|
|
|
|
val data = try {
|
|
|
|
dataResponse.getJSONObject("data").getJSONObject("data")
|
|
|
|
dataResponse.getJSONObject("data").getJSONObject("data")
|
|
|
|
} catch (e: Exception) {
|
|
|
|
} catch (e: Exception) {
|
|
|
|
throw Exception("Cant parse image data. Token may be invalid or expired: ${e.message}")
|
|
|
|
throw ParseException("Cant parse image data. Token may be invalid or expired: ${e.message}", dataUrl, e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val preferredRes = config[preferredImageResolutionKey] ?: "1280"
|
|
|
|
val preferredRes = config[preferredImageResolutionKey] ?: "1280"
|
|
|
|
@ -383,16 +385,12 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedImageId == null || selectedPublicKey == null) {
|
|
|
|
if (selectedImageId == null || selectedPublicKey == null) {
|
|
|
|
throw Exception("Cant find image data")
|
|
|
|
throw ParseException("Cant find image data", dataUrl)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val imagesResponse = try {
|
|
|
|
val imagesResponse = webClient.httpGet(
|
|
|
|
webClient.httpGet(
|
|
|
|
"https://$apiSuffix/books/data/$id/$key/$selectedImageId/$selectedPublicKey/$selectedQuality?crt=$currentToken",
|
|
|
|
"https://$apiSuffix/books/data/$id/$key/$selectedImageId/$selectedPublicKey/$selectedQuality?crt=$currentToken"
|
|
|
|
|
|
|
|
).parseJson()
|
|
|
|
).parseJson()
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
|
|
|
throw Exception("Cant fetch image data: ${e.message}")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val base = imagesResponse.getString("base")
|
|
|
|
val base = imagesResponse.getString("base")
|
|
|
|
val entries = imagesResponse.getJSONArray("entries")
|
|
|
|
val entries = imagesResponse.getJSONArray("entries")
|
|
|
|
@ -408,68 +406,27 @@ internal class Koharu(context: MangaLoaderContext) :
|
|
|
|
url = fullImageUrl,
|
|
|
|
url = fullImageUrl,
|
|
|
|
preview = null,
|
|
|
|
preview = null,
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return pages
|
|
|
|
return pages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun fetchTags(): Set<MangaTag> {
|
|
|
|
private suspend fun fetchTags(namespace: Int): Set<MangaTag> =
|
|
|
|
val url = "https://$apiSuffix/books/tags/filters"
|
|
|
|
webClient.httpGet("https://$apiSuffix/books/tags/filters").parseJsonArray().mapJSONNotNullToSet {
|
|
|
|
val json = try {
|
|
|
|
if (it.getIntOrDefault("namespace", 0) != namespace) {
|
|
|
|
webClient.httpGet(url).parseJson()
|
|
|
|
null
|
|
|
|
} catch (e: Exception) {
|
|
|
|
} else {
|
|
|
|
return emptySet()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val tagsArray = json.optJSONArray("tags") ?: return emptySet()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return tagsArray.mapJSONNotNullToSet { tagObject ->
|
|
|
|
|
|
|
|
if (tagObject.has("namespace")) {
|
|
|
|
|
|
|
|
return@mapJSONNotNullToSet null
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (tagObject.has("id") && tagObject.has("name")) {
|
|
|
|
|
|
|
|
MangaTag(
|
|
|
|
MangaTag(
|
|
|
|
key = tagObject.getString("id"),
|
|
|
|
title = it.getStringOrNull("name")
|
|
|
|
title = tagObject.getString("name").toTitleCase(sourceLocale),
|
|
|
|
?.toTitleCase(sourceLocale) ?: return@mapJSONNotNullToSet null,
|
|
|
|
source = source
|
|
|
|
key = it.getStringOrNull("id") ?: return@mapJSONNotNullToSet null,
|
|
|
|
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
|
|
|
|
null
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun fetchAuthors(): Map<String, String> {
|
|
|
|
|
|
|
|
if (authorMap != null) {
|
|
|
|
|
|
|
|
return authorMap!!
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val url = "https://$apiSuffix/books/tags/filters"
|
|
|
|
|
|
|
|
val json = try {
|
|
|
|
|
|
|
|
webClient.httpGet(url).parseJson()
|
|
|
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
|
|
|
return emptyMap()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val tagsArray = json.optJSONArray("tags") ?: return emptyMap()
|
|
|
|
|
|
|
|
val result = mutableMapOf<String, String>()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (i in 0 until tagsArray.length()) {
|
|
|
|
private suspend fun fetchAuthorsIds(): Map<String, String> = fetchTags(namespace = 1)
|
|
|
|
val tagObject = tagsArray.getJSONObject(i)
|
|
|
|
.associate { it.title.lowercase() to it.key }
|
|
|
|
|
|
|
|
|
|
|
|
if (tagObject.has("namespace") && tagObject.getInt("namespace") == 1) {
|
|
|
|
|
|
|
|
if (tagObject.has("id") && tagObject.has("name")) {
|
|
|
|
|
|
|
|
val id = tagObject.getString("id")
|
|
|
|
|
|
|
|
val name = tagObject.getString("name")
|
|
|
|
|
|
|
|
result[name] = id
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
authorMap = result
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|