[Koharu] Fixes

Koitharu 1 year ago
parent fecb1db3be
commit b24741678c
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -1,475 +1,432 @@
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(
"0" to "Lowest Quality", "0" to "Lowest Quality",
"780" to "Low Quality (780px)", "780" to "Low Quality (780px)",
"980" to "Medium Quality (980px)", "980" to "Medium Quality (980px)",
"1280" to "High Quality (1280px)", "1280" to "High Quality (1280px)",
"1600" to "Highest Quality (1600px)", "1600" to "Highest Quality (1600px)",
), ),
defaultValue = "1280", defaultValue = "1280",
) )
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) { override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys) super.onCreateConfig(keys)
keys.add(userAgentKey) keys.add(userAgentKey)
keys.add(preferredImageResolutionKey) keys.add(preferredImageResolutionKey)
} }
override fun getRequestHeaders() = super.getRequestHeaders().newBuilder() override fun getRequestHeaders() = super.getRequestHeaders().newBuilder()
.add("referer", "https://$domain/") .add("referer", "https://$domain/")
.add("origin", "https://$domain") .add("origin", "https://$domain")
.build() .build()
override val availableSortOrders: Set<SortOrder> = EnumSet.of( override val availableSortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.NEWEST, SortOrder.NEWEST,
SortOrder.POPULARITY, SortOrder.POPULARITY,
SortOrder.POPULARITY_TODAY, SortOrder.POPULARITY_TODAY,
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
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isMultipleTagsSupported = true, isMultipleTagsSupported = true,
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> {
val baseUrl = "https://$apiSuffix/books" val baseUrl = "https://$apiSuffix/books"
val url = buildString { val url = buildString {
append(baseUrl) append(baseUrl)
val terms: MutableList<String> = mutableListOf() val terms: MutableList<String> = mutableListOf()
val includedTags: MutableList<String> = mutableListOf() val includedTags: MutableList<String> = mutableListOf()
val excludedTags: MutableList<String> = mutableListOf() val excludedTags: MutableList<String> = mutableListOf()
if (!filter.query.isNullOrEmpty() && filter.query.startsWith("id:")) { if (!filter.query.isNullOrEmpty() && filter.query.startsWith("id:")) {
val ipk = filter.query.removePrefix("id:") val ipk = filter.query.removePrefix("id:")
val response = webClient.httpGet("$baseUrl/detail/$ipk").parseJson() val response = webClient.httpGet("$baseUrl/detail/$ipk").parseJson()
return listOf(parseMangaDetail(response)) return listOf(parseMangaDetail(response))
} }
val sortValue = when (order) { val sortValue = when (order) {
SortOrder.POPULARITY, SortOrder.POPULARITY_TODAY -> "8" SortOrder.POPULARITY, SortOrder.POPULARITY_TODAY -> "8"
SortOrder.POPULARITY_WEEK -> "9" SortOrder.POPULARITY_WEEK -> "9"
SortOrder.ALPHABETICAL -> "2" SortOrder.ALPHABETICAL -> "2"
SortOrder.ALPHABETICAL_DESC -> "2" SortOrder.ALPHABETICAL_DESC -> "2"
SortOrder.RATING -> "3" SortOrder.RATING -> "3"
SortOrder.NEWEST -> "4" SortOrder.NEWEST -> "4"
else -> "4" else -> "4"
} }
append("?sort=").append(sortValue) append("?sort=").append(sortValue)
if (!filter.query.isNullOrEmpty()) { if (!filter.query.isNullOrEmpty()) {
terms.add("title:\"${filter.query.urlEncoded()}\"") terms.add("title:\"${filter.query.urlEncoded()}\"")
} }
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) } else {
} else { terms.add("artist:\"${filter.author.urlEncoded()}\"")
terms.add("artist:\"${filter.author.urlEncoded()}\"") }
} }
}
filter.tags.forEach { tag ->
filter.tags.forEach { tag -> if (tag.key.startsWith("-")) {
if (tag.key.startsWith("-")) { excludedTags.add(tag.key.substring(1))
excludedTags.add(tag.key.substring(1)) } else {
} else { includedTags.add(tag.key)
includedTags.add(tag.key) }
} }
}
if (excludedTags.isNotEmpty()) {
if (excludedTags.isNotEmpty()) { append("&exclude=").append(excludedTags.joinToString(","))
append("&exclude=").append(excludedTags.joinToString(",")) append("&e=1")
append("&e=1") }
}
if (includedTags.isNotEmpty()) {
if (includedTags.isNotEmpty()) { append("&include=").append(includedTags.joinToString(","))
append("&include=").append(includedTags.joinToString(",")) append("&i=1")
append("&i=1") }
}
append("&page=").append(page)
append("&page=").append(page)
if (terms.isNotEmpty()) {
if (terms.isNotEmpty()) { append("&s=").append(terms.joinToString(" ").urlEncoded())
append("&s=").append(terms.joinToString(" ").urlEncoded()) }
} }
}
val json = webClient.httpGet(url).parseJson()
val json = webClient.httpGet(url).parseJson() json.getStringOrNull("error")?.let {
if (json.has("error") || json.has("message")) { throw ParseException(it, url)
return emptyList() }
} json.getStringOrNull("message")?.let {
return parseMangaList(json) throw ParseException(it, url)
} }
return parseMangaList(json)
private fun parseMangaList(json: JSONObject): List<Manga> { }
val entries = json.optJSONArray("entries") ?: return emptyList()
val results = ArrayList<Manga>(entries.length()) private fun parseMangaList(json: JSONObject): List<Manga> {
val entries = json.optJSONArray("entries") ?: return emptyList()
for (i in 0 until entries.length()) { val results = ArrayList<Manga>(entries.length())
val entry = entries.getJSONObject(i)
val id = entry.getInt("id") for (i in 0 until entries.length()) {
val key = entry.getString("key") val entry = entries.getJSONObject(i)
val url = "$id/$key" val id = entry.getLong("id")
val key = entry.getString("key")
results.add( val url = "$id/$key"
Manga(
id = generateUid(url), results.add(
url = url, Manga(
publicUrl = "https://$domain/g/$url", id = generateUid(id),
title = entry.getString("title"), url = url,
altTitles = emptySet(), publicUrl = "https://$domain/g/$url",
authors = emptySet(), title = entry.getString("title"),
tags = emptySet(), altTitles = emptySet(),
rating = RATING_UNKNOWN, authors = emptySet(),
state = MangaState.FINISHED, tags = emptySet(),
coverUrl = entry.getJSONObject("thumbnail").getString("path"), rating = RATING_UNKNOWN,
contentRating = ContentRating.ADULT, state = null,
source = source, coverUrl = entry.getJSONObject("thumbnail").getString("path"),
) contentRating = ContentRating.ADULT,
) source = source,
} ),
)
return results }
}
return results
private fun parseMangaDetail(json: JSONObject): Manga { }
val data = json.getJSONObject("data")
val id = data.getInt("id") private fun parseMangaDetail(json: JSONObject): Manga {
val key = data.getString("key") val data = json.getJSONObject("data")
val url = "$id/$key" val id = data.getLong("id")
val key = data.getString("key")
var author: String? = null val url = "$id/$key"
val tags = data.optJSONArray("tags")
if (tags != null) { var author: String? = null
for (i in 0 until tags.length()) { val tags = data.optJSONArray("tags")
val tag = tags.getJSONObject(i) if (tags != null) {
if (tag.getInt("namespace") == 1) { for (i in 0 until tags.length()) {
author = tag.getString("name") val tag = tags.getJSONObject(i)
break if (tag.getInt("namespace") == 1) {
} author = tag.getString("name")
} break
} }
}
return Manga( }
id = generateUid(url),
url = url, return Manga(
publicUrl = "https://$domain/g/$url", id = generateUid(id),
title = data.getString("title"), url = url,
altTitles = emptySet(), publicUrl = "https://$domain/g/$url",
authors = setOfNotNull(author), title = data.getString("title"),
tags = emptySet(), altTitles = emptySet(),
rating = RATING_UNKNOWN, authors = setOfNotNull(author),
state = MangaState.FINISHED, tags = emptySet(),
coverUrl = data.getJSONObject("thumbnails").getJSONObject("main").getString("path"), rating = RATING_UNKNOWN,
contentRating = ContentRating.ADULT, state = null,
source = source, coverUrl = data.getJSONObject("thumbnails").getJSONObject("main").getString("path"),
) contentRating = ContentRating.ADULT,
} source = source,
)
override suspend fun getDetails(manga: Manga): Manga { }
val url = manga.url
val response = webClient.httpGet("https://$apiSuffix/books/detail/$url").parseJson() override suspend fun getDetails(manga: Manga): Manga {
val url = manga.url
val id = response.getInt("id") val response = webClient.httpGet("https://$apiSuffix/books/detail/$url").parseJson()
val key = response.getString("key")
val mangaUrl = "$id/$key" val id = response.getLong("id")
val key = response.getString("key")
val tagsList = mutableSetOf<MangaTag>() val mangaUrl = "$id/$key"
var author: String? = null
val tags = response.optJSONArray("tags") val tagsList = mutableSetOf<MangaTag>()
var author: String? = null
if (tags != null) { val tags = response.optJSONArray("tags")
for (i in 0 until tags.length()) {
val tag = tags.getJSONObject(i) if (tags != null) {
if (tag.has("namespace")) { for (i in 0 until tags.length()) {
val namespace = tag.getInt("namespace") val tag = tags.getJSONObject(i)
val tagName = tag.getString("name") if (tag.has("namespace")) {
val namespace = tag.getInt("namespace")
when (namespace) { val tagName = tag.getString("name")
1 -> {
author = tagName when (namespace) {
} 1 -> {
0, 3, 8, 9, 10, 12 -> { author = tagName
tagsList.add( }
MangaTag(
key = tagName, 0, 3, 8, 9, 10, 12 -> {
title = tagName.toTitleCase(sourceLocale), tagsList.add(
source = source MangaTag(
) key = tagName,
) title = tagName.toTitleCase(sourceLocale),
} source = source,
} ),
} else { )
val tagName = tag.getString("name") }
tagsList.add( }
MangaTag( } else {
key = tagName, val tagName = tag.getString("name")
title = tagName.toTitleCase(sourceLocale), tagsList.add(
source = source MangaTag(
) key = tagName,
) title = tagName.toTitleCase(sourceLocale),
} source = source,
} ),
} )
}
val description = buildString { }
val created = response.getLongOrDefault("created_at", 0L) }
if (created > 0) {
append("Posted: ").append(SimpleDateFormat("yyyy-MM-dd", Locale.US).format(created)).append("\n") val description = buildString {
} val created = response.getLongOrDefault("created_at", 0L)
if (created > 0) {
val thumbnails = response.getJSONObject("thumbnails") append("<b>Posted:</b> ").append(SimpleDateFormat("yyyy-MM-dd", Locale.US).format(created)).append("\n")
val pageCount = thumbnails.optJSONArray("entries")?.length() ?: 0 }
append("Pages: ").append(pageCount)
} val thumbnails = response.getJSONObject("thumbnails")
val pageCount = thumbnails.optJSONArray("entries")?.length() ?: 0
val thumbnails = response.getJSONObject("thumbnails") append("<b>Pages:</b> ").append(pageCount)
val base = thumbnails.getString("base") }
val mainPath = thumbnails.getJSONObject("main").getString("path")
val coverUrl = base + mainPath val thumbnails = response.getJSONObject("thumbnails")
val base = thumbnails.getString("base")
return Manga( val mainPath = thumbnails.getJSONObject("main").getString("path")
id = generateUid(mangaUrl), val coverUrl = base + mainPath
url = mangaUrl,
publicUrl = "https://$domain/g/$mangaUrl", return Manga(
title = response.getString("title"), id = generateUid(id),
altTitles = emptySet(), url = mangaUrl,
authors = setOfNotNull(author), publicUrl = "https://$domain/g/$mangaUrl",
tags = tagsList, title = response.getString("title"),
rating = RATING_UNKNOWN, altTitles = emptySet(),
state = MangaState.FINISHED, authors = setOfNotNull(author),
description = description, tags = tagsList,
coverUrl = coverUrl, rating = RATING_UNKNOWN,
contentRating = ContentRating.ADULT, state = MangaState.FINISHED,
source = source, description = description,
chapters = listOf( coverUrl = coverUrl,
MangaChapter( contentRating = ContentRating.ADULT,
id = generateUid("$mangaUrl/chapter"), source = source,
title = "Oneshot", chapters = listOf(
number = 1f, MangaChapter(
url = mangaUrl, id = generateUid("$mangaUrl/chapter"),
scanlator = null, title = null,
uploadDate = response.getLongOrDefault("created_at", 0L), number = 1f,
branch = null, url = mangaUrl,
source = source, scanlator = null,
volume = 0, uploadDate = response.getLongOrDefault("created_at", 0L),
) branch = null,
) source = source,
) volume = 0,
} ),
),
private var cachedToken: String? = null )
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val mangaUrl = chapter.url @Volatile
val parts = mangaUrl.split("/") private var cachedToken: String? = null
if (parts.size < 2) {
throw Exception("Invalid URL") override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
} val mangaUrl = chapter.url
val parts = mangaUrl.split('/')
val id = parts[0] if (parts.size < 2) {
val key = parts[1] throw ParseException("Invalid URL", mangaUrl)
}
val detailResponse = webClient.httpGet("https://$apiSuffix/books/detail/$id/$key").parseJson()
val id = parts[0]
var currentToken = detailResponse.optString("crt", cachedToken ?: "") val key = parts[1]
if (currentToken.isEmpty()) { val detailResponse = webClient.httpGet("https://$apiSuffix/books/detail/$id/$key").parseJson()
try {
val noTokenResponse = webClient.httpPost( var currentToken = detailResponse.getStringOrNull("crt") ?: cachedToken
url = "https://$apiSuffix/books/detail/$id/$key".toHttpUrl(),
form = emptyMap(), if (currentToken.isNullOrEmpty()) {
extraHeaders = getRequestHeaders() try {
).parseJson() val noTokenResponse = webClient.httpPost(
url = "https://$apiSuffix/books/detail/$id/$key".toHttpUrl(),
currentToken = noTokenResponse.optString("crt", "") form = emptyMap(),
if (currentToken.isNotEmpty()) { extraHeaders = getRequestHeaders(),
cachedToken = currentToken ).parseJson()
}
} catch (e: Exception) { currentToken = noTokenResponse.getStringOrNull("crt")
throw Exception("Cant get auth token!") if (!currentToken.isNullOrEmpty()) {
} cachedToken = currentToken
} }
} catch (e: Exception) {
if (currentToken.isEmpty()) { throw IllegalStateException("Cant get auth token", e)
throw Exception("Cant get auth token!") }
} }
val dataResponse = try { if (currentToken.isNullOrEmpty()) {
webClient.httpPost( throw IllegalStateException("Cant get auth token")
url = "https://$apiSuffix/books/detail/$id/$key?crt=$currentToken".toHttpUrl(), }
form = emptyMap(),
extraHeaders = getRequestHeaders() val dataUrl = "https://$apiSuffix/books/detail/$id/$key?crt=$currentToken"
).parseJson() val dataResponse = webClient.httpPost(
} catch (e: Exception) { url = dataUrl.toHttpUrl(),
throw Exception("Cant send POST request: ${e.message}. Token may be expired.") form = emptyMap(),
} extraHeaders = getRequestHeaders(),
).parseJson()
val data = try {
dataResponse.getJSONObject("data").getJSONObject("data") val data = try {
} catch (e: Exception) { dataResponse.getJSONObject("data").getJSONObject("data")
throw Exception("Cant parse image data. Token may be invalid or expired: ${e.message}") } catch (e: Exception) {
} throw ParseException("Cant parse image data. Token may be invalid or expired: ${e.message}", dataUrl, e)
}
val preferredRes = config[preferredImageResolutionKey] ?: "1280"
val resolutionOrder = when (preferredRes) { val preferredRes = config[preferredImageResolutionKey] ?: "1280"
"1600" -> listOf("1600", "1280", "0", "980", "780") val resolutionOrder = when (preferredRes) {
"1280" -> listOf("1280", "1600", "0", "980", "780") "1600" -> listOf("1600", "1280", "0", "980", "780")
"980" -> listOf("980", "1280", "0", "1600", "780") "1280" -> listOf("1280", "1600", "0", "980", "780")
"780" -> listOf("780", "980", "0", "1280", "1600") "980" -> listOf("980", "1280", "0", "1600", "780")
else -> listOf("0", "1600", "1280", "980", "780") "780" -> listOf("780", "980", "0", "1280", "1600")
} else -> listOf("0", "1600", "1280", "980", "780")
}
var selectedImageId: Int? = null
var selectedPublicKey: String? = null var selectedImageId: Int? = null
var selectedQuality = "0" var selectedPublicKey: String? = null
var selectedQuality = "0"
for (res in resolutionOrder) {
if (data.has(res) && !data.isNull(res)) { for (res in resolutionOrder) {
val resData = data.getJSONObject(res) if (data.has(res) && !data.isNull(res)) {
if (resData.has("id") && resData.has("key")) { val resData = data.getJSONObject(res)
selectedImageId = resData.getInt("id") if (resData.has("id") && resData.has("key")) {
selectedPublicKey = resData.getString("key") selectedImageId = resData.getInt("id")
selectedQuality = res selectedPublicKey = resData.getString("key")
break selectedQuality = res
} break
} }
} }
}
if (selectedImageId == null || selectedPublicKey == null) {
throw Exception("Cant find image data") if (selectedImageId == null || selectedPublicKey == null) {
} throw ParseException("Cant find image data", dataUrl)
}
val imagesResponse = try {
webClient.httpGet( val imagesResponse = 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 entries = imagesResponse.getJSONArray("entries")
val base = imagesResponse.getString("base") val pages = ArrayList<MangaPage>(entries.length())
val entries = imagesResponse.getJSONArray("entries") for (i in 0 until entries.length()) {
val imagePath = entries.getJSONObject(i).getString("path")
val pages = ArrayList<MangaPage>(entries.length()) val fullImageUrl = "$base$imagePath"
for (i in 0 until entries.length()) {
val imagePath = entries.getJSONObject(i).getString("path") pages.add(
val fullImageUrl = "$base$imagePath" MangaPage(
id = generateUid(fullImageUrl),
pages.add( url = fullImageUrl,
MangaPage( preview = null,
id = generateUid(fullImageUrl), source = source,
url = fullImageUrl, ),
preview = null, )
source = source, }
)
) return pages
} }
return pages private suspend fun fetchTags(namespace: Int): Set<MangaTag> =
} webClient.httpGet("https://$apiSuffix/books/tags/filters").parseJsonArray().mapJSONNotNullToSet {
if (it.getIntOrDefault("namespace", 0) != namespace) {
private suspend fun fetchTags(): Set<MangaTag> { null
val url = "https://$apiSuffix/books/tags/filters" } else {
val json = try { MangaTag(
webClient.httpGet(url).parseJson() title = it.getStringOrNull("name")
} catch (e: Exception) { ?.toTitleCase(sourceLocale) ?: return@mapJSONNotNullToSet null,
return emptySet() key = it.getStringOrNull("id") ?: return@mapJSONNotNullToSet null,
} source = source,
)
val tagsArray = json.optJSONArray("tags") ?: return emptySet() }
}
return tagsArray.mapJSONNotNullToSet { tagObject ->
if (tagObject.has("namespace")) { private suspend fun fetchAuthorsIds(): Map<String, String> = fetchTags(namespace = 1)
return@mapJSONNotNullToSet null .associate { it.title.lowercase() to it.key }
} }
if (tagObject.has("id") && tagObject.has("name")) {
MangaTag(
key = tagObject.getString("id"),
title = tagObject.getString("name").toTitleCase(sourceLocale),
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()) {
val tagObject = tagsArray.getJSONObject(i)
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
}
}

Loading…
Cancel
Save