Initial commit

This commit is contained in:
2026-03-06 02:42:41 +09:00
commit b782b4659f
39 changed files with 1289 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidApplication)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.composeCompiler)
alias(libs.plugins.composeHotReload)
}
kotlin {
androidTarget {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
jvm()
sourceSets {
androidMain.dependencies {
implementation(libs.compose.uiToolingPreview)
implementation(libs.androidx.activity.compose)
}
commonMain.dependencies {
implementation(libs.compose.runtime)
implementation(libs.compose.foundation)
implementation(libs.compose.material3)
implementation(libs.compose.ui)
implementation(libs.compose.components.resources)
implementation(libs.compose.uiToolingPreview)
implementation(libs.androidx.lifecycle.viewmodelCompose)
implementation(libs.androidx.lifecycle.runtimeCompose)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.cio)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ksoup)
implementation(libs.kotlinx.datetime)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
jvmMain.dependencies {
implementation(compose.desktop.currentOs)
implementation(libs.kotlinx.coroutinesSwing)
}
}
}
android {
namespace = "yenru0.yrkaier.nue"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
applicationId = "yenru0.yrkaier.nue"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
testImplementation(libs.junit.jupiter)
debugImplementation(libs.compose.uiTooling)
}
compose.desktop {
application {
mainClass = "yenru0.yrkaier.nue.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "yenru0.yrkaier.nue"
packageVersion = "1.0.0"
}
}
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@android:style/Theme.Material.Light.NoActionBar">
<activity
android:exported="true"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,25 @@
package yenru0.yrkaier.nue
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
setContent {
App()
}
}
}
@Preview
@Composable
fun AppAndroidPreview() {
App()
}

View File

@@ -0,0 +1,9 @@
package yenru0.yrkaier.nue
import android.os.Build
class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}"
}
actual fun getPlatform(): Platform = AndroidPlatform()

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0"/>
<item
android:color="#00000000"
android:offset="1.0"/>
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000"/>
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF"/>
</vector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">nue</string>
</resources>

View File

@@ -0,0 +1,44 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="450dp"
android:height="450dp"
android:viewportWidth="64"
android:viewportHeight="64">
<path
android:pathData="M56.25,18V46L32,60 7.75,46V18L32,4Z"
android:fillColor="#6075f2"/>
<path
android:pathData="m41.5,26.5v11L32,43V60L56.25,46V18Z"
android:fillColor="#6b57ff"/>
<path
android:pathData="m32,43 l-9.5,-5.5v-11L7.75,18V46L32,60Z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="23.131"
android:centerY="18.441"
android:gradientRadius="42.132"
android:type="radial">
<item android:offset="0" android:color="#FF5383EC"/>
<item android:offset="0.867" android:color="#FF7F52FF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M22.5,26.5 L32,21 41.5,26.5 56.25,18 32,4 7.75,18Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="44.172"
android:startY="4.377"
android:endX="17.973"
android:endY="34.035"
android:type="linear">
<item android:offset="0" android:color="#FF33C3FF"/>
<item android:offset="0.878" android:color="#FF5383EC"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m32,21 l9.526,5.5v11L32,43 22.474,37.5v-11z"
android:fillColor="#000000"/>
</vector>

View File

@@ -0,0 +1,49 @@
package yenru0.yrkaier.nue
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import org.jetbrains.compose.resources.painterResource
import nue.composeapp.generated.resources.Res
import nue.composeapp.generated.resources.compose_multiplatform
@Composable
@Preview
fun App() {
MaterialTheme {
var showContent by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.primaryContainer)
.safeContentPadding()
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(onClick = { showContent = !showContent }) {
Text("Click me!")
}
AnimatedVisibility(showContent) {
val greeting = remember { Greeting().greet() }
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(painterResource(Res.drawable.compose_multiplatform), null)
Text("Compose: $greeting")
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
package yenru0.yrkaier.nue
class Greeting {
private val platform = getPlatform()
fun greet(): String {
return "Hello, ${platform.name}!"
}
}

View File

@@ -0,0 +1,7 @@
package yenru0.yrkaier.nue
interface Platform {
val name: String
}
expect fun getPlatform(): Platform

View File

@@ -0,0 +1,20 @@
package yenru0.yrkaier.nue.data
import kotlinx.datetime.LocalDate
data class NovelInfo(
val id: Int,
var title: String,
var author: String,
var views: Int,
var recommendations: Int,
var tags: List<String>,
var prefers: Int,
var synopsis: String
)
data class EpisodeInfo(
val episodeId: Int,
val title: String,
val uploadDate: LocalDate
)

View File

@@ -0,0 +1,194 @@
package yenru0.yrkaier.nue.data
import com.fleeksoft.ksoup.Ksoup
import io.ktor.client.HttpClient
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.request.post
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.isSuccess
import kotlinx.coroutines.delay
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.number
import kotlinx.datetime.todayIn
import kotlin.time.Clock
class NpiaDownloader {
val loginkey: String
init {
val chars = "0123456789abcdef"
val firstPart = (1..32).map { chars.random() }.joinToString("")
val secondPart = (1..32).map { chars.random() }.joinToString("")
loginkey = "${firstPart}_$secondPart"
}
object Endpoints {
private 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"
}
val client = HttpClient {
install(DefaultRequest) {
header(
HttpHeaders.UserAgent,
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
header(
HttpHeaders.Accept,
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
)
}
install(ContentNegotiation) {
}
}
suspend fun login(email: String, passwd: String): Boolean {
val response = client.post("https://novelpia.com/proc/login") {
header(HttpHeaders.ContentType, "application/x-www-form-urlencoded; charset=UTF-8")
header(HttpHeaders.Cookie, "LOGINKEY=$loginkey")
setBody("redirectrurl=&email=$email&wd=$passwd")
}
val body = response.bodyAsText()
return body.contains("감사합니다")
}
suspend fun logout(): Boolean {
val response = client.post("https://novelpia.com/proc/logout") {
header(HttpHeaders.ContentType, "application/x-www-form-urlencoded; charset=UTF-8")
header(HttpHeaders.Cookie, "LOGINKEY=$loginkey")
}
return response.status.isSuccess()
}
suspend fun getNovelInfo(id: Int): NovelInfo {
val response = client.get("https://novelpia.com/novel/$id") {
header(HttpHeaders.Cookie, "LOGINKEY=$loginkey")
}
val html = response.bodyAsText()
val document = Ksoup.parse(html)
val title = document.select(".epnew-novel-title").text().trim()
val author = document.select(".epnew-writer > .in-writer > .writer-name").text().trim()
val spans = document.select(".info-count1 > .counter-line-a span")
val views = spans.getOrNull(1)?.text()?.replace(",", "")?.toInt() ?: 0
val recommendations = spans.getOrNull(3)?.text()?.replace(",", "")?.toInt() ?: 0
val tags = document.select(".epnew-tag > .writer-tag > .tag").map { it.text() }
val prefers = document.selectFirst(".info-count2 .writer-name")?.text()?.replace(",", "")?.toInt() ?: 0
val synopsis = document.select(".epnew-novel-info .synopsis").text().trim()
return NovelInfo(
id = id,
title = title,
author = author,
views = views,
recommendations = recommendations,
tags = tags,
prefers = prefers,
synopsis = synopsis
)
}
suspend fun getEpisodeList(novelId: Int, page: Int): List<EpisodeInfo> {
val episodeList = mutableListOf<EpisodeInfo>()
val data = "novel_no=$novelId&sort=DOWN&page=$page"
val response = client.post(Endpoints.EPISODE_LIST_URL) {
header(HttpHeaders.ContentType, "application/x-www-form-urlencoded; charset=UTF-8")
header(HttpHeaders.Cookie, "LOGINKEY=$loginkey")
setBody(data)
}
if (!response.status.isSuccess()) {
throw Exception("Failed to fetch episode list: ${response.status}")
}
val html = response.bodyAsText()
val document = Ksoup.parse(html)
val episodes = document.select("tr.ep_style5")
for (ep in episodes) {
val episodeId = ep.attr("data-episode-no").toInt()
val episodeTitle = ep.selectFirst("td.font12 > b")!!.ownText()
val dateString = ep.selectFirst("td.font12 > div.ep_style2 > font > span > b")!!.text()
val (year, month, day) = if (dateString.contains("시간전")) {
val today = Clock.System.todayIn(TimeZone.of("Asia/Seoul"))
Triple(today.year, today.month.number, today.day)
} else {
val parts = dateString.trim().split('.').map { it.trim().toIntOrNull() ?: 0 }
Triple(
parts[0] + 2000,
parts[1],
parts[2]
)
}
val uploadDate = LocalDate(year, month, day)
episodeList.add(
EpisodeInfo(
episodeId = episodeId,
title = episodeTitle,
uploadDate = uploadDate
)
)
}
return episodeList
}
suspend fun getAllEpisodeList(novelId: Int): List<EpisodeInfo> {
val episodeList = mutableListOf<EpisodeInfo>()
val episodeIdSet = mutableSetOf<Int>()
var page = 0
var hasMoreContent = true
while (hasMoreContent) {
val episodes = getEpisodeList(novelId, page)
if (episodes.isEmpty()) {
hasMoreContent = false
} else {
var cnt = 0
for (ep in episodes) {
if (ep.episodeId !in episodeIdSet) {
episodeList.add(ep)
episodeIdSet.add(ep.episodeId)
cnt += 1
}
}
if (cnt == 0) {
hasMoreContent = false
}
}
delay(300L)
page++
}
return episodeList
}
suspend fun downloadEpisode(novelId: Int, episodeId: Int) {
client.request("https://novelpia.com/novel/$novelId") {
header(HttpHeaders.Cookie, "LOGINKEY=$loginkey")
}
}
}

View File

@@ -0,0 +1,12 @@
package yenru0.yrkaier.nue
import kotlin.test.Test
import kotlin.test.assertEquals
class ComposeAppCommonTest {
@Test
fun example() {
assertEquals(3, 1 + 2)
}
}

View File

@@ -0,0 +1,46 @@
package yenru0.yrkaier.nue.data
import kotlinx.coroutines.runBlocking
import kotlin.test.Test
const email = ""
const passwd = ""
class NpiaTest {
@Test
fun testGetSession() {
if (email.isEmpty() || passwd.isEmpty()) {
println("Please set email and password before testing")
return
}
val downloader = NpiaDownloader()
runBlocking {
val res = downloader.login(email, passwd)
if (res) {
println("Login successful, loginkey: ${downloader.loginkey}")
} else {
println("Login failed")
return@runBlocking
}
val id = 396851
val info = downloader.getNovelInfo(id)
val eps = downloader.getAllEpisodeList(id)
println("Title: ${info.title}")
println("Author: ${info.author}")
println("Views: ${info.views}")
println("Rcmms: ${info.recommendations}")
println("Tags: ${info.tags.joinToString(", ")}")
println("Prefers: ${info.prefers}")
println("Synopsis: ${info.synopsis}")
for (ep in eps) {
println("epid: ${ep.episodeId}, title: ${ep.title}, date: ${ep.uploadDate}")
}
downloader.logout()
}
}
}

View File

@@ -0,0 +1,7 @@
package yenru0.yrkaier.nue
class JVMPlatform : Platform {
override val name: String = "Java ${System.getProperty("java.version")}"
}
actual fun getPlatform(): Platform = JVMPlatform()

View File

@@ -0,0 +1,13 @@
package yenru0.yrkaier.nue
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
fun main() = application {
Window(
onCloseRequest = ::exitApplication,
title = "nue",
) {
App()
}
}