Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 78 additions & 53 deletions src/main/kotlin/org/dokiteam/doki/parsers/site/id/Ikiru.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.dokiteam.doki.parsers.site.id

import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.json.JSONArray
import org.jsoup.nodes.Document
import org.dokiteam.doki.parsers.MangaLoaderContext
Expand Down Expand Up @@ -39,7 +40,7 @@ import java.util.Locale
internal class Ikiru(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.IKIRU, 24, 24) {

override val configKeyDomain = ConfigKey.Domain("01.ikiru.wtf")
override val configKeyDomain = ConfigKey.Domain("02.ikiru.wtf")
override val sourceLocale: Locale = Locale.ENGLISH

override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
Expand All @@ -57,7 +58,7 @@ internal class Ikiru(context: MangaLoaderContext) :
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isMultipleTagsSupported = true,
isTagsExclusionSupported = false,
isTagsExclusionSupported = true,
isSearchSupported = true,
isSearchWithFiltersSupported = true,
)
Expand All @@ -79,34 +80,41 @@ internal class Ikiru(context: MangaLoaderContext) :
private suspend fun getNonce(): String {
if (nonce == null) {
val json =
webClient.httpGet("https://${domain}/ajax-call?type=search_form&action=get_nonce")
webClient.httpGet("https://${domain}/wp-admin/admin-ajax.php?type=search_form&action=get_nonce")
val html = json.parseHtml()
val nonceValue = html.select("input[name=search_nonce]").attr("value")
nonce = nonceValue
}
return nonce!!
}


override suspend fun getListPage(
page: Int,
order: SortOrder,
filter: MangaListFilter,
): List<Manga> {
val url = "https://${domain}/ajax-call"
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = "https://${domain}/wp-admin/admin-ajax.php?action=advanced_search"

val formParts = mutableMapOf<String, String>()
formParts["action"] = "advanced_search"
formParts["page"] = page.toString()
formParts["nonce"] = getNonce()
formParts["nonce"] = getNonce()

filter.query?.let { formParts["query"] = it }
formParts["inclusion"] = "AND"
if (filter.tags.isNotEmpty()) {
val genreArray = JSONArray(filter.tags.map { it.key })
formParts["genre"] = genreArray.toString()
} else formParts["genre"] = "[]"

if (filter.tags.isNotEmpty()) {
formParts["genre-relation"] = "AND"
val genreArray = JSONArray(filter.tags.map { it.key })
formParts["genre"] = genreArray.toString()
}
formParts["exclusion"] = "AND"
if (filter.tagsExclude.isNotEmpty()) {
val exGenreArray = JSONArray(filter.tags.map { it.key })
formParts["genre_exclude"] = exGenreArray.toString()
} else formParts["genre_exclude"] = "[]"

formParts["page"] = page.toString()

if (!filter.author.isNullOrEmpty()) {
val authorArray = JSONArray(filter.author)
formParts["author"] = authorArray.toString()
} else formParts["author"] = "[]"

formParts["artist"] = "[]"
formParts["project"] = "0"

if (filter.types.isNotEmpty()) {
val typeArray = JSONArray()
Expand All @@ -120,10 +128,10 @@ internal class Ikiru(context: MangaLoaderContext) :
else -> {}
}
}
if (typeArray.length() > 0) {
formParts["type"] = typeArray.toString()
}
}
formParts["type"] = typeArray.toString()
} else {
formParts["type"] = "[]"
}

if (filter.states.isNotEmpty()) {
val statusArray = JSONArray()
Expand All @@ -138,13 +146,11 @@ internal class Ikiru(context: MangaLoaderContext) :
if (statusArray.length() > 0) {
formParts["status"] = statusArray.toString()
}
}

if (!filter.author.isNullOrEmpty()) {
val authorArray = JSONArray(filter.author)
formParts["series-author"] = authorArray.toString()
}
} else {
formParts["status"] = "[]"
}

formParts["order"] = "desc"
formParts["orderby"] = when (order) {
SortOrder.UPDATED -> "updated"
SortOrder.POPULARITY -> "popular"
Expand All @@ -153,8 +159,14 @@ internal class Ikiru(context: MangaLoaderContext) :
else -> "popular"
}

if (!filter.query.isNullOrEmpty()) {
filter.query.let { formParts["query"] = it }
} else {
formParts["query"] = "[]"
}

val html = webClient.httpPost(url, form = formParts).parseHtml()
val extraHeaders = Headers.headersOf("Content-Type", "multipart/form-data")
val html = webClient.httpPost(url.toHttpUrl(), form = formParts, extraHeaders = extraHeaders).parseHtml()
return parseMangaList(html)
}

Expand Down Expand Up @@ -288,11 +300,13 @@ internal class Ikiru(context: MangaLoaderContext) :

val headers = Headers.Companion.headersOf(
"hx-request", "true",
"hx-target", "chapter-list",
"hx-trigger", "chapter-list",
"Referer", mangaAbsoluteUrl,
)

while (true) {
val url = "https://${domain}/ajax-call?manga_id=$mangaId&page=$page&action=chapter_list"
val url = "https://${domain}/wp-admin/admin-ajax.php?manga_id=$mangaId&page=$page&action=chapter_list"
val doc = webClient.httpGet(url, headers).parseHtml()

val chapterElements = doc.select("div#chapter-list > div[data-chapter-number]")
Expand Down Expand Up @@ -340,28 +354,39 @@ internal class Ikiru(context: MangaLoaderContext) :
}
}

private suspend fun fetchAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://${domain}/advanced-search/").parseHtml()

return doc.select("[data-genre], .genre-item").mapNotNullToSet { element ->
val key = element.attr("data-genre").ifEmpty {
element.selectFirst("input")?.attr("value")
} ?: return@mapNotNullToSet null

val title = element.text().ifEmpty {
element.selectFirst("label")?.text()
} ?: return@mapNotNullToSet null

MangaTag(
key = key,
title = title.toTitleCase(),
source = source,
)
}
}


private fun parseDate(dateStr: String?): Long {
private suspend fun fetchAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://${domain}/advanced-search/").parseHtml()
val scriptContent = doc.select("script")
.firstOrNull { it.data().contains("var searchTerms") }
?.data()
?: return emptySet()

val jsonString = scriptContent
.substringAfter("var searchTerms =")
.substringBeforeLast(";")
.trim()

val json = org.json.JSONObject(jsonString)
val genreObject = json.optJSONObject("genre") ?: return emptySet()
val tags = mutableSetOf<MangaTag>()

for (key in genreObject.keys()) {
val item = genreObject.optJSONObject(key) ?: continue
val taxonomy = item.optString("taxonomy")
if (taxonomy != "genre") continue
val slug = item.optString("slug").takeIf { it.isNotBlank() } ?: continue
val name = item.optString("name").takeIf { it.isNotBlank() } ?: continue

tags += MangaTag(
title = name.toTitleCase(),
key = slug,
source = source
)
}
return tags
}

private fun parseDate(dateStr: String?): Long {
if (dateStr.isNullOrEmpty()) return 0

return try {
Expand Down