From f602523d02d6eb0cc78e5e6abe154ae33bf42c292e24a42e1bece4dde3988394 Mon Sep 17 00:00:00 2001 From: yenru0 Date: Sat, 7 Mar 2026 23:58:53 +0900 Subject: [PATCH] Add viewer_data download --- .../yenru0/yrkaier/nue/data/NpiaDownloader.kt | 57 +++++++++++++------ .../yenru0/yrkaier/nue/data/NpiaTest.kt | 3 +- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/yenru0/yrkaier/nue/data/NpiaDownloader.kt b/composeApp/src/commonMain/kotlin/yenru0/yrkaier/nue/data/NpiaDownloader.kt index 43449ee..c384c80 100644 --- a/composeApp/src/commonMain/kotlin/yenru0/yrkaier/nue/data/NpiaDownloader.kt +++ b/composeApp/src/commonMain/kotlin/yenru0/yrkaier/nue/data/NpiaDownloader.kt @@ -11,12 +11,18 @@ import io.ktor.client.request.request import io.ktor.client.request.setBody import io.ktor.client.statement.bodyAsText import io.ktor.http.HttpHeaders +import io.ktor.http.contentLength import io.ktor.http.isSuccess +import io.ktor.serialization.kotlinx.json.json import kotlinx.coroutines.delay import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.number import kotlinx.datetime.todayIn +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import kotlin.time.Clock class NpiaDownloader { @@ -31,12 +37,13 @@ class NpiaDownloader { } object Endpoints { - private const val BASE_URL = "https://novelpia.com" + const val BASE_URL = "https://novelpia.com" const val PROC_URL = "$BASE_URL/proc" const val LOGIN_URL = "$PROC_URL/login" const val LOGOUT_URL = "$PROC_URL/logout" const val EPISODE_LIST_URL = "$PROC_URL/episode_list" const val NOVEL_URL = "$BASE_URL/novel" + const val SCRIPT_URL = "$PROC_URL/viewer_data" } val client = HttpClient { @@ -52,7 +59,11 @@ class NpiaDownloader { } install(ContentNegotiation) { - + json(Json { + prettyPrint = true + isLenient = true + ignoreUnknownKeys = true + }) } } @@ -185,26 +196,38 @@ class NpiaDownloader { } suspend fun downloadEpisode(episodeId: Int): List { - val res = client.request("https://novelpia.com/novel/viewer/$episodeId") { + val data = "size=14" + val res = client.post("${Endpoints.SCRIPT_URL}/$episodeId") { header(HttpHeaders.Cookie, "LOGINKEY=$loginkey") + header(HttpHeaders.Origin, Endpoints.BASE_URL) + header(HttpHeaders.ContentType, "application/x-www-form-urlencoded; charset=UTF-8") + header(HttpHeaders.Referrer, "${Endpoints.BASE_URL}/viewer/$episodeId") + setBody(data) } + if (!res.status.isSuccess()) { throw Exception("Failed to fetch episode content: ${res.status}") } - println(res.bodyAsText()) - return listOf() - val document = Ksoup.parse(res.bodyAsText()) - val draw = document.select("#novel_drawing").first()!!; - val lines: List = draw.select(".line").map { - val imgsel = it.select("img") - if (imgsel.isNotEmpty()) { - val imageUrl = imgsel[0].attr("abs:src").ifEmpty { imgsel[0].attr("src") } - ScriptLineData.Image(imageUrl) - } else { - ScriptLineData.Html(it.html()) - } - } - return lines + val json = Json.parseToJsonElement(res.bodyAsText()).jsonObject + + json["s"]!!.let { scriptJson -> + val lines = mutableListOf() + for (line in scriptJson.jsonArray) { + val lineText = line.jsonObject["text"]!!.jsonPrimitive.content.replace(" ", " ").trimEnd().replace(Regex("

.*?

"), "") + if (lineText.startsWith("